{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "e7f22162",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | default_exp _docusaurus_helper"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e51a8972",
   "metadata": {},
   "source": [
    "# Docusaurus Helper"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "aca4b3f1",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "import itertools\n",
    "import re\n",
    "import ast\n",
    "import types\n",
    "from inspect import Signature, getmembers, isclass, isfunction, signature, ismethod, getsource, Parameter\n",
    "from pathlib import Path\n",
    "from typing import *\n",
    "from urllib.parse import urljoin\n",
    "from functools import lru_cache\n",
    "\n",
    "import typer\n",
    "from docstring_parser import parse\n",
    "from docstring_parser.common import DocstringParam, DocstringRaises, DocstringReturns, Docstring\n",
    "from nbdev.config import get_config\n",
    "from nbdev.quarto import nbdev_readme\n",
    "from nbdev.doclinks import NbdevLookup, patch_name, L, _find_mod\n",
    "from nbdev_mkdocs.mkdocs import (\n",
    "    _add_all_submodules,\n",
    "    _import_all_members,\n",
    "    _import_functions_and_classes,\n",
    "    _import_submodules\n",
    ")\n",
    "from nbdev_mkdocs._helpers.doc_links_utils import fix_sym_links as update_default_symbol_links"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "4ab4b0cd",
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "import functools\n",
    "import random\n",
    "import shutil\n",
    "from tempfile import TemporaryDirectory\n",
    "from contextlib import contextmanager\n",
    "from abc import abstractmethod\n",
    "from unittest.mock import patch, MagicMock\n",
    "import textwrap\n",
    "\n",
    "\n",
    "import pytest\n",
    "from pydantic import BaseModel\n",
    "from aiokafka import ConsumerRecord\n",
    "from aiokafka.coordinator.assignors.roundrobin import RoundRobinPartitionAssignor\n",
    "\n",
    "from fastkafka._components.asyncapi import ConsumeCallable"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "23776e14",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _get_return_annotation(s: Signature) -> str:\n",
    "    \"\"\"Get the return annotation from the function signature.\n",
    "\n",
    "    Args:\n",
    "        s: The signature of the function from which the annotations must be extracted.\n",
    "\n",
    "    Returns:\n",
    "        The return annotation, or an empty string if not available.\n",
    "\n",
    "    \"\"\"\n",
    "    if s.return_annotation == None or \"inspect._empty\" in str(s.return_annotation):\n",
    "        return \"\"\n",
    "    if isinstance(s.return_annotation, str):\n",
    "        return s.return_annotation\n",
    "    ret_val: str = (\n",
    "        str(s.return_annotation).replace(\"typing.\", \"\").replace(\"NoneType\", \"None\")\n",
    "        if \"typing.\" in str(s.return_annotation)\n",
    "        else str(s.return_annotation.__name__)\n",
    "    )\n",
    "    return ret_val"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "727346e4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# def fixture() -> Callable[[ConsumeCallable], ConsumeCallable]:\n",
    "#     pass\n",
    "\n",
    "# _signature = signature(fixture)\n",
    "# actual = _get_return_annotation(_signature)\n",
    "# expected = \"Callable[[ConsumeCallable], ConsumeCallable]\"\n",
    "# print(actual)\n",
    "\n",
    "# assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e18ddea5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "\n",
    "def fixture():\n",
    "    pass\n",
    "\n",
    "_signature = signature(fixture)\n",
    "actual = _get_return_annotation(_signature)\n",
    "expected = \"\"\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0281b48b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Callable[[aiokafka.structs.ConsumerRecord], Awaitable[None]]\n"
     ]
    }
   ],
   "source": [
    "\n",
    "def fixture() -> Callable[[ConsumerRecord], Awaitable[None]]:\n",
    "    pass\n",
    "\n",
    "_signature = signature(fixture)\n",
    "actual = _get_return_annotation(_signature)\n",
    "expected = \"Callable[[aiokafka.structs.ConsumerRecord], Awaitable[None]]\"\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a22acf60",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Callable[[ForwardRef('FastAPI')], AsyncIterator[None]]\n"
     ]
    }
   ],
   "source": [
    "def fixture() -> Callable[[\"FastAPI\"], AsyncIterator[None]]:\n",
    "    pass\n",
    "\n",
    "_signature = signature(fixture)\n",
    "actual = _get_return_annotation(_signature)\n",
    "expected = \"Callable[[ForwardRef('FastAPI')], AsyncIterator[None]]\"\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b3a0fc1c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "EventMetadata\n"
     ]
    }
   ],
   "source": [
    "def fixture() -> \"EventMetadata\":\n",
    "    pass\n",
    "\n",
    "_signature = signature(fixture)\n",
    "actual = _get_return_annotation(_signature)\n",
    "expected = \"EventMetadata\"\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a61a1211",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Optional[str]\n"
     ]
    }
   ],
   "source": [
    "def fixture() -> Optional[str]:\n",
    "    pass\n",
    "\n",
    "\n",
    "_signature = signature(fixture)\n",
    "actual = _get_return_annotation(_signature)\n",
    "expected = (\n",
    "    \"Union[str, None]\"\n",
    "    if f\"{sys.version_info.major}.{sys.version_info.minor}\" == \"3.8\"\n",
    "    else \"Optional[str]\"\n",
    ")\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected, expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4110e572",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Iterable[str]\n"
     ]
    }
   ],
   "source": [
    "def fixture() -> Iterable[str]:\n",
    "    pass\n",
    "\n",
    "_signature = signature(fixture)\n",
    "actual = _get_return_annotation(_signature)\n",
    "expected = \"Iterable[str]\"\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d8667128",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "def fixture() -> None:\n",
    "    pass\n",
    "\n",
    "_signature = signature(fixture)\n",
    "actual = _get_return_annotation(_signature)\n",
    "expected = \"\"\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "14bf2727",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "str\n"
     ]
    }
   ],
   "source": [
    "def fixture() -> str:\n",
    "    pass\n",
    "\n",
    "_signature = signature(fixture)\n",
    "actual = _get_return_annotation(_signature)\n",
    "expected = \"str\"\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f8a74b6b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "List[int]\n"
     ]
    }
   ],
   "source": [
    "def fixture() -> List[int]:\n",
    "    pass\n",
    "\n",
    "_signature = signature(fixture)\n",
    "actual = _get_return_annotation(_signature)\n",
    "expected = \"List[int]\"\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0d376675",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dict[str, Any]\n"
     ]
    }
   ],
   "source": [
    "def fixture() -> Dict[str, Any]:\n",
    "    pass\n",
    "\n",
    "_signature = signature(fixture)\n",
    "actual = _get_return_annotation(_signature)\n",
    "expected = \"Dict[str, Any]\"\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "067d1134",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Union[str, List[str]]\n"
     ]
    }
   ],
   "source": [
    "\n",
    "def fixture() -> Union[str, List[str]]:\n",
    "    pass\n",
    "\n",
    "_signature = signature(fixture)\n",
    "actual = _get_return_annotation(_signature)\n",
    "expected = \"Union[str, List[str]]\"\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "73960f27",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A\n"
     ]
    }
   ],
   "source": [
    "class A:\n",
    "    pass\n",
    "\n",
    "\n",
    "def fixture() -> A:\n",
    "    pass\n",
    "\n",
    "_signature = signature(fixture)\n",
    "actual = _get_return_annotation(_signature)\n",
    "expected = \"A\"\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "51b09ae5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Callable[[], Any]\n"
     ]
    }
   ],
   "source": [
    "\n",
    "\n",
    "def fixture() -> Callable[[], Any]:\n",
    "    pass\n",
    "\n",
    "_signature = signature(fixture)\n",
    "actual = _get_return_annotation(_signature)\n",
    "expected = \"Callable[[], Any]\"\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dffc79a4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _get_param_annotation(param: Parameter) -> str:\n",
    "    \"\"\"Get the annotation of a function parameter.\n",
    "\n",
    "    Args:\n",
    "        param: The parameter object.\n",
    "\n",
    "    Returns:\n",
    "        The parameter annotation, or an empty string if not available.\n",
    "\n",
    "    \"\"\"\n",
    "\n",
    "    if \"typing.\" in str(param.annotation):\n",
    "        return f'`{str(param.annotation).replace(\"typing.\", \"\")}`'\n",
    "    elif isinstance(param.annotation, str):\n",
    "        return param.annotation\n",
    "    else:\n",
    "        return (\n",
    "            \"\"\n",
    "            if param.annotation.__name__ == \"_empty\"\n",
    "            else f\"`{param.annotation.__name__}`\"\n",
    "        )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "17cce765",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['`int`', '`Optional[str]`', '`Dict[str, int]`', 'Any']\n"
     ]
    }
   ],
   "source": [
    "def f(a: int, b: Optional[str], c: Dict[str, int], d: \"Any\"):\n",
    "    pass\n",
    "\n",
    "s = signature(f)\n",
    "\n",
    "actual = []\n",
    "expected = (\n",
    "    ['`int`', '`Union[str, NoneType]`', '`Dict[str, int]`', 'Any']\n",
    "    if f\"{sys.version_info.major}.{sys.version_info.minor}\" == \"3.8\"\n",
    "    else ['`int`', '`Optional[str]`', '`Dict[str, int]`', 'Any']\n",
    ")\n",
    "for param in s.parameters.values():\n",
    "    actual.append(_get_param_annotation(param))\n",
    "    \n",
    "print(actual)\n",
    "assert sorted(actual) == sorted(expected)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "47aceb4d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "def _get_default_value(param: Parameter) -> str:\n",
    "    \"\"\"Get the default value of the function parameter.\n",
    "\n",
    "    Args:\n",
    "        param: The parameter object.\n",
    "\n",
    "    Returns:\n",
    "        The default value of the function parameter.\n",
    "\n",
    "    \"\"\"\n",
    "    if param.default is param.empty:\n",
    "        return \"*required*\"\n",
    "    \n",
    "    return f\"`'{param.default}'`\" if isinstance(param.default, str) else f\"`{param.default}`\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "53835059",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['*required*', \"`'default_string'`\", '`{}`', \"`(1, 'string', 2.0)`\"]\n"
     ]
    }
   ],
   "source": [
    "def fixture(\n",
    "    arg_1: int,\n",
    "    *,\n",
    "    arg_2: str = \"default_string\",\n",
    "    arg_3: Dict[str, int] = {},\n",
    "    arg_4: Tuple[int, str, float] = (1, \"string\", 2.0),\n",
    ") -> str:\n",
    "    pass\n",
    "\n",
    "\n",
    "s = signature(fixture)\n",
    "expected = ['*required*', \"`'default_string'`\", '`{}`', \"`(1, 'string', 2.0)`\"]\n",
    "actual = []\n",
    "for param in s.parameters.values():\n",
    "    actual.append(_get_default_value(param))\n",
    "    \n",
    "print(actual)\n",
    "assert sorted(actual) == sorted(expected)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1c7a07ca",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _get_params_annotation(s: Signature) -> Dict[str, Dict[str, str]]:\n",
    "    \"\"\"Get the annotations along with its default values for the parameters of the symbol.\n",
    "\n",
    "    Args:\n",
    "        s: The signature of the function from which the annotations must be extracted.\n",
    "\n",
    "    Returns:\n",
    "        The parameter annotations along with its default value.\n",
    "    \"\"\"\n",
    "    return {\n",
    "        f\"{param.name}\": {\n",
    "            \"type\": _get_param_annotation(param),\n",
    "            \"default\": _get_default_value(param),\n",
    "        }\n",
    "        for param in s.parameters.values()\n",
    "    }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0d361cd4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'arg_1': {'type': '`int`', 'default': '*required*'},\n",
       " 'arg_2': {'type': '`str`', 'default': \"`'default_string'`\"},\n",
       " 'arg_3': {'type': '`Dict[str, int]`', 'default': '`{}`'},\n",
       " 'arg_4': {'type': '`Optional[float]`', 'default': '`None`'},\n",
       " 'arg_5': {'type': '`Tuple[int, str, float]`',\n",
       "  'default': \"`(1, 'string', 2.0)`\"},\n",
       " 'arg_6': {'type': '`List[Union[int, str]]`', 'default': \"`[1, 'string']`\"},\n",
       " 'arg_7': {'type': '`Set[int]`', 'default': '`{1, 2, 3}`'},\n",
       " 'arg_8': {'type': '`str`', 'default': \"`'string'`\"},\n",
       " 'arg_9': {'type': '`Dict[str, str]`', 'default': '`{}`'},\n",
       " 'arg_10': {'type': '', 'default': '`None`'}}"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def fixture(\n",
    "    arg_1: int,\n",
    "    *,\n",
    "    arg_2: str = \"default_string\",\n",
    "    arg_3: Dict[str, int] = {},\n",
    "    arg_4: Optional[float] = None,\n",
    "    arg_5: Tuple[int, str, float] = (1, \"string\", 2.0),\n",
    "    arg_6: List[Union[int, str]] = [1, \"string\"],\n",
    "    arg_7: Set[int] = {1, 2, 3},\n",
    "    arg_8: str = \"string\",\n",
    "    arg_9: Dict[str, str] = {},\n",
    "    arg_10 = None\n",
    ") -> str:\n",
    "    pass\n",
    "\n",
    "\n",
    "_signature = signature(fixture)\n",
    "actual = _get_params_annotation(_signature)\n",
    "\n",
    "_optional_type = \"Union[float, NoneType]\" if f\"{sys.version_info.major}.{sys.version_info.minor}\" == \"3.8\" else \"Optional[float]\"\n",
    "expected = {\n",
    "    \"arg_1\": {\"type\": \"`int`\", \"default\": \"*required*\"},\n",
    "    \"arg_2\": {\"type\": \"`str`\", \"default\": \"`'default_string'`\"},\n",
    "    \"arg_3\": {\"type\": \"`Dict[str, int]`\", \"default\": \"`{}`\"},\n",
    "    \"arg_4\": {\"type\": f\"`{_optional_type}`\", \"default\": \"`None`\"},\n",
    "    \"arg_5\": {\"type\": \"`Tuple[int, str, float]`\", \"default\": \"`(1, 'string', 2.0)`\"},\n",
    "    \"arg_6\": {\"type\": \"`List[Union[int, str]]`\", \"default\": \"`[1, 'string']`\"},\n",
    "    \"arg_7\": {\"type\": \"`Set[int]`\", \"default\": \"`{1, 2, 3}`\"},\n",
    "    \"arg_8\": {\"type\": \"`str`\", \"default\": \"`'string'`\"},\n",
    "    \"arg_9\": {\"type\": \"`Dict[str, str]`\", \"default\": \"`{}`\"},\n",
    "    \"arg_10\": {\"type\": \"\", \"default\": \"`None`\"},\n",
    "}\n",
    "display(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f470e0a0",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _generate_parameters_table(\n",
    "    symbol_annotations: Dict[str, Union[Dict[str, str], str]],\n",
    "    section_items: Union[List[DocstringParam]],\n",
    "    section_name: str,\n",
    ") -> str:\n",
    "    \"\"\"Generate parameter table in markdown format\n",
    "    \n",
    "    Args:\n",
    "        symbol_annotations: Symbol annotations along with its default value\n",
    "        section_items: The parameter section of a parsed docstring\n",
    "        section_name: The name of the section\n",
    "\n",
    "    Returns:\n",
    "        The parameters of a symbol in markdown-formatted string\n",
    "    \"\"\"\n",
    "    nl = \"\\n\"\n",
    "    _section_template = (\n",
    "        \"|  Name | Type | Description | Default |\\n|---|---|---|---|\\n{section_body}\\n\"\n",
    "    )\n",
    "    section_body = \"\".join(\n",
    "        [\n",
    "            f'| `{section.arg_name}` | {symbol_annotations[\"parameters\"][section.arg_name][\"type\"]} | {section.description.replace(nl, \"\")} | {symbol_annotations[\"parameters\"][section.arg_name][\"default\"]} |\\n' # type: ignore\n",
    "            if section.arg_name in symbol_annotations[\"parameters\"]\n",
    "            else \"\"\n",
    "            for section in section_items\n",
    "        ]\n",
    "    )\n",
    "    return f\"**{section_name}**:\\n\\n\" + _section_template.format(\n",
    "        section_body=section_body,\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "70f444db",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "**Parameters**:\n",
      "\n",
      "|  Name | Type | Description | Default |\n",
      "|---|---|---|---|\n",
      "| `name` | str | name of the person | *required* |\n",
      "| `age` | int | age of the person | *required* |\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "params_dict = {\n",
    "    \"name\": {\"type\": \"str\", \"default\": \"*required*\"},\n",
    "    \"age\": {\"type\": \"int\", \"default\": \"*required*\"},\n",
    "}\n",
    "return_dict = \"str\"\n",
    "signature_dict = {\"parameters\": params_dict, \"return\": return_dict}\n",
    "\n",
    "fixture_docstring = parse(\"\"\"This is a docstring for a sample function.\n",
    "\n",
    "    Args:\n",
    "        name: name of the person\n",
    "        age: age of the person\n",
    "    \"\"\")\n",
    "\n",
    "actual = _generate_parameters_table(\n",
    "    signature_dict, fixture_docstring.params, \"Parameters\"\n",
    ")\n",
    "\n",
    "expected = \"\"\"**Parameters**:\n",
    "\n",
    "|  Name | Type | Description | Default |\n",
    "|---|---|---|---|\n",
    "| `name` | str | name of the person | *required* |\n",
    "| `age` | int | age of the person | *required* |\n",
    "\n",
    "\"\"\"\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cf5f49d4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _generate_return_and_raises_table(\n",
    "    symbol_annotations: Dict[str, Union[Dict[str, str], str]],\n",
    "    section_items: Union[List[DocstringReturns], List[DocstringRaises]],\n",
    "    section_name: str,\n",
    ") -> str:\n",
    "    \"\"\"Generate return and raises table in markdown format\n",
    "    \n",
    "    Args:\n",
    "        symbol_annotations: Symbol annotations along with its default value\n",
    "        section_items: The parameter section of a parsed docstring\n",
    "        section_name: The name of the section\n",
    "        \n",
    "    Returns:\n",
    "        The return and raises section of a symbol in markdown-formatted string\n",
    "    \"\"\"\n",
    "    nl = \"\\n\"\n",
    "    _section_template = \"|  Type | Description |\\n|---|---|\\n{section_body}\\n\"\n",
    "    section_body = \"\".join(\n",
    "        [\n",
    "            f'| `{symbol_annotations[\"return\"] if section_name == \"Returns\" else section.type_name}` | {section.description.replace(nl, \"\")} |\\n' # type: ignore\n",
    "            for section in section_items\n",
    "        ]\n",
    "    )\n",
    "    return f\"**{section_name}**:\\n\\n\" + _section_template.format(\n",
    "        section_body=section_body,\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f14971a8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "**Returns**:\n",
      "\n",
      "|  Type | Description |\n",
      "|---|---|\n",
      "| `str` | A formatted string |\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "params_dict = {\n",
    "    \"name\": {\"type\": \"str\", \"default\": \"*required*\"},\n",
    "    \"age\": {\"type\": \"int\", \"default\": \"*required*\"},\n",
    "}\n",
    "return_dict = \"str\"\n",
    "signature_dict = {\"parameters\": params_dict, \"return\": return_dict}\n",
    "\n",
    "fixture_docstring = parse(\"\"\"This is a docstring for a sample function.\n",
    "\n",
    "    Returns:\n",
    "        A formatted string\n",
    "    \"\"\")\n",
    "\n",
    "actual = _generate_return_and_raises_table(\n",
    "    signature_dict, fixture_docstring.many_returns, \"Returns\"\n",
    ")\n",
    "\n",
    "expected = \"\"\"**Returns**:\n",
    "\n",
    "|  Type | Description |\n",
    "|---|---|\n",
    "| `str` | A formatted string |\n",
    "\n",
    "\"\"\"\n",
    "print(actual)\n",
    "assert actual == expected, actual"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1ce3f080",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "**Exceptions**:\n",
      "\n",
      "|  Type | Description |\n",
      "|---|---|\n",
      "| `ValueError` | If name is not a string |\n",
      "| `TypeError` | If type is invalid |\n",
      "| `KeyError` | If key name is invalid |\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "params_dict = {\n",
    "    \"name\": {\"type\": \"str\", \"default\": \"*required*\"},\n",
    "    \"age\": {\"type\": \"int\", \"default\": \"*required*\"},\n",
    "}\n",
    "return_dict = \"str\"\n",
    "signature_dict = {\"parameters\": params_dict, \"return\": return_dict}\n",
    "\n",
    "fixture_docstring = parse(\"\"\"This is a docstring for a sample function.\n",
    "\n",
    "    Raises:\n",
    "        ValueError: If name is not a string\n",
    "        TypeError: If type is invalid\n",
    "        KeyError: If key name is invalid\n",
    "    \"\"\")\n",
    "\n",
    "actual = _generate_return_and_raises_table(\n",
    "    signature_dict, fixture_docstring.raises, \"Exceptions\"\n",
    ")\n",
    "\n",
    "expected = \"\"\"**Exceptions**:\n",
    "\n",
    "|  Type | Description |\n",
    "|---|---|\n",
    "| `ValueError` | If name is not a string |\n",
    "| `TypeError` | If type is invalid |\n",
    "| `KeyError` | If key name is invalid |\n",
    "\n",
    "\"\"\"\n",
    "print(actual)\n",
    "assert actual == expected, actual"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "696bcb7f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _format_docstring_section_items(\n",
    "    symbol_annotations: Dict[str, Union[Dict[str, str], str]],\n",
    "    section_items: Union[\n",
    "        List[DocstringParam], List[DocstringReturns], List[DocstringRaises]\n",
    "    ],\n",
    "    section_name: str,\n",
    ") -> str:\n",
    "    \"\"\"Format the docstring sections in a table format\n",
    "    \n",
    "    Args:\n",
    "        symbol_annotations: Symbol annotations along with its default value\n",
    "        section_items: The parameter section of a parsed docstring\n",
    "        section_name: The name of the section\n",
    "        \n",
    "    Returns:\n",
    "        The docstring sections of the symbol in markdown-formatted string\n",
    "    \"\"\"\n",
    "    if section_name == \"Parameters\":\n",
    "        return _generate_parameters_table(symbol_annotations, section_items, section_name) # type: ignore\n",
    "    else:\n",
    "        return _generate_return_and_raises_table(symbol_annotations, section_items, section_name) # type: ignore"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dbdaccee",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "**Parameters**:\n",
      "\n",
      "|  Name | Type | Description | Default |\n",
      "|---|---|---|---|\n",
      "| `name` | str | name of the person | *required* |\n",
      "| `age` | int | age of the person | *required* |\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "params_dict = {\n",
    "    \"name\": {\"type\": \"str\", \"default\": \"*required*\"},\n",
    "    \"age\": {\"type\": \"int\", \"default\": \"*required*\"},\n",
    "}\n",
    "return_dict = \"str\"\n",
    "signature_dict = {\"parameters\": params_dict, \"return\": return_dict}\n",
    "\n",
    "fixture_docstring = parse(\"\"\"This is a docstring for a sample function.\n",
    "\n",
    "    It can contain multiple lines and can include *markdown* syntax.\n",
    "\n",
    "    Args:\n",
    "        name: name of the person\n",
    "        age: age of the person\n",
    "        some_param: some_param of the person\n",
    "\n",
    "    Returns:\n",
    "        A formatted string\n",
    "\n",
    "    Raises:\n",
    "        ValueError: If name is not a string\n",
    "    \"\"\")\n",
    "\n",
    "actual = _format_docstring_section_items(\n",
    "    signature_dict, fixture_docstring.params, \"Parameters\"\n",
    ")\n",
    "\n",
    "expected = \"\"\"**Parameters**:\n",
    "\n",
    "|  Name | Type | Description | Default |\n",
    "|---|---|---|---|\n",
    "| `name` | str | name of the person | *required* |\n",
    "| `age` | int | age of the person | *required* |\n",
    "\n",
    "\"\"\"\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3385419e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "**Parameters**:\n",
      "\n",
      "|  Name | Type | Description | Default |\n",
      "|---|---|---|---|\n",
      "| `name` | str | name of the person | *required* |\n",
      "| `age` | int | age of the person | *required* |\n",
      "| `some_param` | Dict[str, int] | some_param of the person | `{}` |\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "params_dict = {\n",
    "    \"name\": {\"type\": \"str\", \"default\": \"*required*\"},\n",
    "    \"age\": {\"type\": \"int\", \"default\": \"*required*\"},\n",
    "    \"some_param\": {\"type\": \"Dict[str, int]\", \"default\": \"`{}`\"},\n",
    "}\n",
    "return_dict = \"str\"\n",
    "signature_dict = {\"parameters\": params_dict, \"return\": return_dict}\n",
    "\n",
    "fixture_docstring = parse(\"\"\"This is a docstring for a sample function.\n",
    "\n",
    "    It can contain multiple lines and can include *markdown* syntax.\n",
    "\n",
    "    Args:\n",
    "        name: name of the person\n",
    "        age: age of the person\n",
    "        some_param: some_param of the person\n",
    "\n",
    "    Returns:\n",
    "        A formatted string\n",
    "\n",
    "    Raises:\n",
    "        ValueError: If name is not a string\n",
    "    \"\"\")\n",
    "\n",
    "actual = _format_docstring_section_items(\n",
    "    signature_dict, fixture_docstring.params, \"Parameters\"\n",
    ")\n",
    "\n",
    "expected = \"\"\"**Parameters**:\n",
    "\n",
    "|  Name | Type | Description | Default |\n",
    "|---|---|---|---|\n",
    "| `name` | str | name of the person | *required* |\n",
    "| `age` | int | age of the person | *required* |\n",
    "| `some_param` | Dict[str, int] | some_param of the person | `{}` |\n",
    "\n",
    "\"\"\"\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "859594bc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "**Returns**:\n",
      "\n",
      "|  Type | Description |\n",
      "|---|---|\n",
      "| `str` | A formatted string |\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "actual = _format_docstring_section_items(\n",
    "    signature_dict, fixture_docstring.many_returns, \"Returns\"\n",
    ")\n",
    "\n",
    "expected = \"\"\"**Returns**:\n",
    "\n",
    "|  Type | Description |\n",
    "|---|---|\n",
    "| `str` | A formatted string |\n",
    "\n",
    "\"\"\"\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f386d0a5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "**Exceptions**:\n",
      "\n",
      "|  Type | Description |\n",
      "|---|---|\n",
      "| `ValueError` | If name is not a string |\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "actual = _format_docstring_section_items(\n",
    "    signature_dict, fixture_docstring.raises, \"Exceptions\"\n",
    ")\n",
    "expected = \"\"\"**Exceptions**:\n",
    "\n",
    "|  Type | Description |\n",
    "|---|---|\n",
    "| `ValueError` | If name is not a string |\n",
    "\n",
    "\"\"\"\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0293aabf",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _get_annotation(symbol: Type) -> Dict[str, Union[Dict[str, Dict[str, str]], str]]:\n",
    "    \"\"\"Get annotations along with its default value for a symbol\n",
    "    \n",
    "    Args:\n",
    "        symbol: The symbol for which the annotations needs to be extracted\n",
    "        \n",
    "    Returns:\n",
    "        The annotations dict along with its default value\n",
    "    \"\"\"\n",
    "    symbol = symbol.fget if isinstance(symbol, property) else symbol\n",
    "    symbol_signature = signature(symbol)\n",
    "    params_dict = _get_params_annotation(symbol_signature)\n",
    "    return_annotation = _get_return_annotation(symbol_signature)\n",
    "    return {\n",
    "        \"parameters\": params_dict,\n",
    "        \"return\": return_annotation\n",
    "    }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ac8d74a6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'parameters': {'name': {'type': '`str`', 'default': '*required*'},\n",
       "  'info': {'type': '`Tuple[int, str, float]`', 'default': '`80`'},\n",
       "  'contact': {'type': '`Optional[str]`', 'default': '`None`'}},\n",
       " 'return': 'str'}"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "@property\n",
    "def fixture(name: str, info: Tuple[int, str, float] = 80, contact: Optional[str] = None) -> str:\n",
    "    \"\"\"This is a docstring for a sample function.\n",
    "\n",
    "    Args:\n",
    "        name: name of the person\n",
    "        info: info of the person\n",
    "        contact: optional contact for the documentation. If None, the\n",
    "            contact will be set to placeholder values:\n",
    "            name='Author' url=HttpUrl('https://www.google.com', ) email='noreply@gmail.com'\n",
    "\n",
    "    \"\"\"\n",
    "    pass\n",
    "\n",
    "_optional_type = \"Union[str, NoneType]\" if f\"{sys.version_info.major}.{sys.version_info.minor}\" == \"3.8\" else \"Optional[str]\"\n",
    "expected = {\n",
    "    \"parameters\": {\n",
    "        \"name\": {\"type\": \"`str`\", \"default\": \"*required*\"},\n",
    "        \"info\": {\"type\": \"`Tuple[int, str, float]`\", \"default\": \"`80`\"},\n",
    "        \"contact\": {\"type\": f\"`{_optional_type}`\", \"default\": \"`None`\"},\n",
    "    },\n",
    "    \"return\": \"str\"\n",
    "}\n",
    "actual = _get_annotation(fixture)\n",
    "display(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cef46c80",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'parameters': {'name': {'type': '`str`', 'default': '*required*'},\n",
       "  'info': {'type': '`Union[str, List[str]]`', 'default': '*required*'},\n",
       "  'contact': {'type': '`Optional[str]`', 'default': \"`'default_email.com'`\"}},\n",
       " 'return': ''}"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def fixture(name: str, info: Union[str, List[str]], contact: Optional[str] = \"default_email.com\"):\n",
    "    \"\"\"This is a docstring for a sample function.\n",
    "\n",
    "    Args:\n",
    "        name: name of the person\n",
    "        info: info of the person\n",
    "        contact: optional contact for the documentation. If None, the\n",
    "            contact will be set to placeholder values:\n",
    "            name='Author' url=HttpUrl('https://www.google.com', ) email='noreply@gmail.com'\n",
    "\n",
    "    \"\"\"\n",
    "    pass\n",
    "\n",
    "_optional_type = \"Union[str, NoneType]\" if f\"{sys.version_info.major}.{sys.version_info.minor}\" == \"3.8\" else \"Optional[str]\"\n",
    "expected = {\n",
    "    \"parameters\": {\n",
    "        \"name\": {\"type\": \"`str`\", \"default\": \"*required*\"},\n",
    "        \"info\": {\"type\": \"`Union[str, List[str]]`\", \"default\": \"*required*\"},\n",
    "        \"contact\": {\"type\": f\"`{_optional_type}`\", \"default\": \"`'default_email.com'`\"}\n",
    "    },\n",
    "    \"return\": \"\"\n",
    "}\n",
    "actual = _get_annotation(fixture)\n",
    "display(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1f1a4f22",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _format_docstring_sections(\n",
    "    symbol: Type, parsed_docstring: Docstring\n",
    ") -> str:\n",
    "    \"\"\"Format the parsed docstring sections into markdown-formatted table\n",
    "\n",
    "    Args:\n",
    "        symbol: The symbol for which to parse the docstring.\n",
    "        parsed_docstring: A Docstring object\n",
    "\n",
    "    Returns:\n",
    "        The markdown-formatted docstring.\n",
    "    \"\"\"\n",
    "    symbol_annotations = _get_annotation(symbol)\n",
    "    formatted_docstring = \"\"\n",
    "    sections = [\n",
    "        (\"Parameters\", parsed_docstring.params),\n",
    "        (\"Returns\", parsed_docstring.many_returns),\n",
    "        (\"Exceptions\", parsed_docstring.raises),\n",
    "    ]\n",
    "\n",
    "    for section_name, section_items in sections:\n",
    "        if len(section_items) > 0:  # type: ignore\n",
    "            formatted_docstring += _format_docstring_section_items(\n",
    "                symbol_annotations, section_items, section_name  # type: ignore\n",
    "            )\n",
    "\n",
    "    return formatted_docstring"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d585ca4d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "**Parameters**:\n",
      "\n",
      "|  Name | Type | Description | Default |\n",
      "|---|---|---|---|\n",
      "| `name` | `str` | name of the person | *required* |\n",
      "| `age` | `int` | age of the person | *required* |\n",
      "\n",
      "**Exceptions**:\n",
      "\n",
      "|  Type | Description |\n",
      "|---|---|\n",
      "| `ValueError` | If name is not a string |\n",
      "| `TypeError` | If name is not a string |\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "def fixture(name: str, age: int):\n",
    "    \"\"\"\n",
    "    This is a docstring for a sample function.\n",
    "\n",
    "    It can contain multiple lines and can include *markdown* syntax.\n",
    "\n",
    "    Args:\n",
    "        name: name of the person\n",
    "        age: age of the person\n",
    "\n",
    "    Raises:\n",
    "        ValueError: If name is not a string\n",
    "        TypeError: If name is not a string\n",
    "    \"\"\"\n",
    "    pass\n",
    "\n",
    "parsed_docstring = parse(fixture.__doc__)\n",
    "\n",
    "expected = \"\"\"**Parameters**:\n",
    "\n",
    "|  Name | Type | Description | Default |\n",
    "|---|---|---|---|\n",
    "| `name` | `str` | name of the person | *required* |\n",
    "| `age` | `int` | age of the person | *required* |\n",
    "\n",
    "**Exceptions**:\n",
    "\n",
    "|  Type | Description |\n",
    "|---|---|\n",
    "| `ValueError` | If name is not a string |\n",
    "| `TypeError` | If name is not a string |\n",
    "\n",
    "\"\"\"\n",
    "\n",
    "actual = _format_docstring_sections(fixture, parsed_docstring)\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e06e2b05",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _format_free_links(s: str) -> str:\n",
    "    \"\"\"Format free links in a given string by adding proper spacing around them.\n",
    "\n",
    "    Args:\n",
    "        s: The input string containing free links.\n",
    "\n",
    "    Returns:\n",
    "        The modified string with properly formatted free links.\n",
    "    \"\"\"\n",
    "    pattern = r\"([\\\"'])(https?:\\/\\/[^\\s]+)([\\\"'])\"\n",
    "    ret_val = re.sub(\n",
    "        pattern, lambda match: f\"{match.group(1)} {match.group(2)} {match.group(3)}\", s\n",
    "    )\n",
    "    return ret_val"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "83f6543a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Click [here](https://www.example.com) to explore Example\n",
      "Learn more at [Example](http://www.example.co.in)\n",
      "Discover at [https://www.example.edu](https://www.example.edu)\n",
      "[Example](https://www.example.co.uk) is worth exploring\n",
      "\n",
      "url=HttpUrl(' https://www.google.com ', )\n",
      "url=HttpUrl(' http://www.example.net ', )\n",
      "\" https://www.google.edu \"\n",
      "\n"
     ]
    }
   ],
   "source": [
    "fixture = \"\"\"\n",
    "Click [here](https://www.example.com) to explore Example\n",
    "Learn more at [Example](http://www.example.co.in)\n",
    "Discover at [https://www.example.edu](https://www.example.edu)\n",
    "[Example](https://www.example.co.uk) is worth exploring\n",
    "\n",
    "url=HttpUrl('https://www.google.com', )\n",
    "url=HttpUrl('http://www.example.net', )\n",
    "\"https://www.google.edu\"\n",
    "\"\"\"\n",
    "expected = \"\"\"\n",
    "Click [here](https://www.example.com) to explore Example\n",
    "Learn more at [Example](http://www.example.co.in)\n",
    "Discover at [https://www.example.edu](https://www.example.edu)\n",
    "[Example](https://www.example.co.uk) is worth exploring\n",
    "\n",
    "url=HttpUrl(' https://www.google.com ', )\n",
    "url=HttpUrl(' http://www.example.net ', )\n",
    "\" https://www.google.edu \"\n",
    "\"\"\"\n",
    "actual = _format_free_links(fixture)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "078ccbed",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _docstring_to_markdown(symbol: Type) -> str:\n",
    "    \"\"\"Converts a docstring to a markdown-formatted string.\n",
    "\n",
    "    Args:\n",
    "        symbol: The symbol for which the documentation needs to be generated in markdown format.\n",
    "\n",
    "    Returns:\n",
    "        The markdown-formatted docstring.\n",
    "    \"\"\"\n",
    "    if symbol.__doc__ is None:\n",
    "        return \"\"\n",
    "\n",
    "    parsed_docstring = parse(symbol.__doc__)\n",
    "    formatted_docstring = f\"{parsed_docstring.short_description}\\n\\n\"\n",
    "    formatted_docstring += (\n",
    "        f\"{parsed_docstring.long_description}\\n\\n\"\n",
    "        if parsed_docstring.long_description\n",
    "        else \"\"\n",
    "    )\n",
    "    formatted_docstring += _format_docstring_sections(symbol, parsed_docstring)\n",
    "    ret_val = _format_free_links(formatted_docstring)\n",
    "\n",
    "    return ret_val"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d304a619",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "def fixture(name: str, age: int, contact: str):\n",
    "    pass\n",
    "\n",
    "expected = \"\"\n",
    "actual = _docstring_to_markdown(fixture)\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dd702f11",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "This is a docstring for a sample function.\n",
      "\n",
      "**Parameters**:\n",
      "\n",
      "|  Name | Type | Description | Default |\n",
      "|---|---|---|---|\n",
      "| `name` | `str` | name of the person | *required* |\n",
      "| `age` | `int` | age of the person | *required* |\n",
      "| `contact` | `str` | optional contact for the documentation. If None, thecontact will be set to placeholder values:name='Author' url=HttpUrl(' https://www.google.com ', ) email='noreply@gmail.com' | *required* |\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "def fixture(name: str, age: int, contact: str):\n",
    "    \"\"\"This is a docstring for a sample function.\n",
    "\n",
    "    Args:\n",
    "        name: name of the person\n",
    "        age: age of the person\n",
    "        contact: optional contact for the documentation. If None, the\n",
    "            contact will be set to placeholder values:\n",
    "            name='Author' url=HttpUrl('https://www.google.com', ) email='noreply@gmail.com'\n",
    "\n",
    "    \"\"\"\n",
    "    pass\n",
    "\n",
    "expected = \"\"\"This is a docstring for a sample function.\n",
    "\n",
    "**Parameters**:\n",
    "\n",
    "|  Name | Type | Description | Default |\n",
    "|---|---|---|---|\n",
    "| `name` | `str` | name of the person | *required* |\n",
    "| `age` | `int` | age of the person | *required* |\n",
    "| `contact` | `str` | optional contact for the documentation. If None, thecontact will be set to placeholder values:name='Author' url=HttpUrl(' https://www.google.com ', ) email='noreply@gmail.com' | *required* |\n",
    "\n",
    "\"\"\"\n",
    "\n",
    "actual = _docstring_to_markdown(fixture)\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9017c683",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "This is a docstring for a sample function.\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "def fixture():\n",
    "    \"\"\"This is a docstring for a sample function.\"\"\"\n",
    "    pass\n",
    "\n",
    "expected = \"\"\"This is a docstring for a sample function.\n",
    "\n",
    "\"\"\"\n",
    "\n",
    "actual = _docstring_to_markdown(fixture)\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3ff970bc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "This is a docstring for a sample function.\n",
      "\n",
      "It can contain multiple lines and can include *markdown* syntax.\n",
      "\n",
      "**Parameters**:\n",
      "\n",
      "|  Name | Type | Description | Default |\n",
      "|---|---|---|---|\n",
      "| `name` | `str` | name of the person | *required* |\n",
      "| `age` | `int` | age of the person | *required* |\n",
      "\n",
      "**Returns**:\n",
      "\n",
      "|  Type | Description |\n",
      "|---|---|\n",
      "| `str` | A formatted string |\n",
      "\n",
      "**Exceptions**:\n",
      "\n",
      "|  Type | Description |\n",
      "|---|---|\n",
      "| `ValueError` | If name is not a string |\n",
      "| `TypeError` | If name is not a string |\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "def fixture(name: str, age: int) -> str:\n",
    "    \"\"\"This is a docstring for a sample function.\n",
    "\n",
    "    It can contain multiple lines and can include *markdown* syntax.\n",
    "\n",
    "    Args:\n",
    "        name: name of the person\n",
    "        age: age of the person\n",
    "\n",
    "    Returns:\n",
    "        A formatted string\n",
    "\n",
    "    Raises:\n",
    "        ValueError: If name is not a string\n",
    "        TypeError: If name is not a string\n",
    "    \"\"\"\n",
    "    pass\n",
    "\n",
    "expected = \"\"\"This is a docstring for a sample function.\n",
    "\n",
    "It can contain multiple lines and can include *markdown* syntax.\n",
    "\n",
    "**Parameters**:\n",
    "\n",
    "|  Name | Type | Description | Default |\n",
    "|---|---|---|---|\n",
    "| `name` | `str` | name of the person | *required* |\n",
    "| `age` | `int` | age of the person | *required* |\n",
    "\n",
    "**Returns**:\n",
    "\n",
    "|  Type | Description |\n",
    "|---|---|\n",
    "| `str` | A formatted string |\n",
    "\n",
    "**Exceptions**:\n",
    "\n",
    "|  Type | Description |\n",
    "|---|---|\n",
    "| `ValueError` | If name is not a string |\n",
    "| `TypeError` | If name is not a string |\n",
    "\n",
    "\"\"\"\n",
    "\n",
    "actual = _docstring_to_markdown(fixture)\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "82fbe401",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _get_submodules(module_name: str) -> List[str]:\n",
    "    \"\"\"Get a list of all submodules contained within the module.\n",
    "\n",
    "    Args:\n",
    "        module_name: The name of the module to retrieve submodules from\n",
    "\n",
    "    Returns:\n",
    "        A list of submodule names within the module\n",
    "    \"\"\"\n",
    "    members = _import_all_members(module_name)\n",
    "    members_with_submodules = _add_all_submodules(members)\n",
    "    members_with_submodules_str: List[str] = [\n",
    "        x[:-1] if x.endswith(\".\") else x for x in members_with_submodules\n",
    "    ]\n",
    "    return members_with_submodules_str"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "75b8c561",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['fastkafka',\n",
       " 'fastkafka.EventMetadata',\n",
       " 'fastkafka.FastKafka',\n",
       " 'fastkafka.KafkaEvent',\n",
       " 'fastkafka.encoder',\n",
       " 'fastkafka.encoder.AvroBase',\n",
       " 'fastkafka.encoder.avro_decoder',\n",
       " 'fastkafka.encoder.avro_encoder',\n",
       " 'fastkafka.encoder.avsc_to_pydantic',\n",
       " 'fastkafka.encoder.json_decoder',\n",
       " 'fastkafka.encoder.json_encoder',\n",
       " 'fastkafka.executors',\n",
       " 'fastkafka.executors.DynamicTaskExecutor',\n",
       " 'fastkafka.executors.SequentialExecutor',\n",
       " 'fastkafka.testing',\n",
       " 'fastkafka.testing.ApacheKafkaBroker',\n",
       " 'fastkafka.testing.LocalRedpandaBroker',\n",
       " 'fastkafka.testing.Tester']"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "module_name = \"fastkafka\"\n",
    "members_with_submodules = _get_submodules(module_name)\n",
    "members_with_submodules"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dec44e68",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _load_submodules(\n",
    "    module_name: str, members_with_submodules: List[str]\n",
    ") -> List[Type]:\n",
    "    \"\"\"Load the given submodules from the module.\n",
    "\n",
    "    Args:\n",
    "        module_name: The name of the module whose submodules to load\n",
    "        members_with_submodules: A list of submodule names to load\n",
    "\n",
    "    Returns:\n",
    "        A list of imported submodule objects.\n",
    "    \"\"\"\n",
    "    submodules = _import_submodules(module_name)\n",
    "    members: List[Tuple[str, Type]] = list(\n",
    "        itertools.chain(*[_import_functions_and_classes(m) for m in submodules])\n",
    "    )\n",
    "    names = [\n",
    "        y\n",
    "        for x, y in members\n",
    "        if f\"{y.__module__}.{y.__name__}\" in members_with_submodules\n",
    "    ]\n",
    "    return names"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a7d3f5b9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[fastkafka.EventMetadata,\n",
       " fastkafka.FastKafka,\n",
       " fastkafka.KafkaEvent,\n",
       " fastkafka.encoder.AvroBase,\n",
       " <function fastkafka.encoder.avro_decoder(raw_msg: bytes, cls: Type[pydantic.main.BaseModel]) -> Any>,\n",
       " <function fastkafka.encoder.avro_encoder(msg: pydantic.main.BaseModel) -> bytes>,\n",
       " <function fastkafka.encoder.avsc_to_pydantic(schema: Dict[str, Any]) -> Type[pydantic.main.BaseModel]>,\n",
       " <function fastkafka.encoder.json_decoder(raw_msg: bytes, cls: Type[pydantic.main.BaseModel]) -> Any>,\n",
       " <function fastkafka.encoder.json_encoder(msg: pydantic.main.BaseModel) -> bytes>,\n",
       " fastkafka.executors.DynamicTaskExecutor,\n",
       " fastkafka.executors.SequentialExecutor,\n",
       " fastkafka.testing.ApacheKafkaBroker,\n",
       " fastkafka.testing.LocalRedpandaBroker,\n",
       " fastkafka.testing.Tester]"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "module_name = \"fastkafka\"\n",
    "members_with_submodules = _get_submodules(module_name)\n",
    "symbols = _load_submodules(module_name, members_with_submodules)\n",
    "symbols"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a307153c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _get_parameters(_signature: Signature) -> List[str]:\n",
    "    \"\"\"Convert a function's signature into a string representation of its parameter list.\n",
    "\n",
    "    Args:\n",
    "        _signature: The signature object representing the function's signature.\n",
    "\n",
    "    Returns:\n",
    "        A list of strings representing the function's parameters, including their default values if applicable.\n",
    "    \"\"\"\n",
    "    params = [param for param in _signature.parameters.values()]\n",
    "    ret_val = [\n",
    "            f\"{param.name}\"\n",
    "            if (param.default is param.empty)\n",
    "            else f\"{param.name}='{param.default}'\"\n",
    "            if isinstance(param.default, str)\n",
    "            else f\"{param.name}={param.default}\"\n",
    "            for param in params\n",
    "        ]\n",
    "    return ret_val"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "37de04bd",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['arg_1', 'arg_2', 'arg_3', 'arg_4=80']\n"
     ]
    }
   ],
   "source": [
    "def fixture_function(\n",
    "    arg_1: str, arg_2, arg_3: Union[Dict[str, str], str], arg_4: Optional[int] = 80\n",
    ") -> str:\n",
    "    pass\n",
    "\n",
    "\n",
    "_signature = signature(fixture_function)\n",
    "\n",
    "expected = ['arg_1', 'arg_2', 'arg_3', 'arg_4=80']\n",
    "actual = _get_parameters(_signature)\n",
    "\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "101243e8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['arg_1', 'arg_2']\n"
     ]
    }
   ],
   "source": [
    "def fixture_function(arg_1: str, arg_2) -> None:\n",
    "    pass\n",
    "\n",
    "\n",
    "_signature = signature(fixture_function)\n",
    "\n",
    "expected = [\"arg_1\", \"arg_2\"]\n",
    "actual = _get_parameters(_signature)\n",
    "\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ca2578a3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['arg_1', \"arg_2='default_string'\", 'arg_3={}', 'arg_4=None', \"arg_5=(1, 'string', 2.0)\", \"arg_6=[1, 'string']\", 'arg_7={1, 2, 3}', \"arg_8='string'\"]\n"
     ]
    }
   ],
   "source": [
    "def fixture_function(\n",
    "    arg_1: int,\n",
    "    *,\n",
    "    arg_2: str = \"default_string\",\n",
    "    arg_3: Dict[str, int] = {},\n",
    "    arg_4: Optional[float] = None,\n",
    "    arg_5: Tuple[int, str, float] = (1, \"string\", 2.0),\n",
    "    arg_6: List[Union[int, str]] = [1, \"string\"],\n",
    "    arg_7: Set[int] = {1,2,3},\n",
    "    arg_8: str = \"string\"\n",
    ") -> None:\n",
    "    pass\n",
    "\n",
    "_signature = signature(fixture_function)\n",
    "params = [param for param in _signature.parameters.values()]\n",
    "expected = ['arg_1', \"arg_2='default_string'\", 'arg_3={}', 'arg_4=None', \"arg_5=(1, 'string', 2.0)\", \"arg_6=[1, 'string']\", 'arg_7={1, 2, 3}', \"arg_8='string'\"]\n",
    "actual = _get_parameters(_signature)\n",
    "\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "995e4b77",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _format_symbol_definition(\n",
    "    symbol: Type, params_list: List[str]\n",
    ") -> str:\n",
    "    \"\"\"Format the given symbol parameters by adding a new line and indentation.\n",
    "\n",
    "    Args:\n",
    "        symbol: The symbol for which the symbol definition needs to be formatted.\n",
    "        params_list: A string representation of the parameter list.\n",
    "\n",
    "    Returns:\n",
    "        A formatted string representation of the parameters with new lines and indentation.\n",
    "    \"\"\"\n",
    "    parameters = \", \".join(params_list)\n",
    "    if parameters == \"\":\n",
    "        return f\"{symbol.__name__}()\\n\"\n",
    "    elif len(f\"{symbol.__name__}({parameters})\") <= 79:\n",
    "        return f\"{symbol.__name__}(\\n    {parameters}\\n)\\n\"\n",
    "    else:\n",
    "        formatted_parameters = \"\".join([f\"\\n    {param},\" for param in params_list])\n",
    "        return f\"{symbol.__name__}({formatted_parameters}\\n)\\n\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c27fb036",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "fixture_function()\n",
      "\n"
     ]
    }
   ],
   "source": [
    "def fixture_function():\n",
    "    pass\n",
    "\n",
    "\n",
    "expected = \"\"\"fixture_function()\n",
    "\"\"\"\n",
    "_signature = signature(fixture_function)\n",
    "parameters = _get_parameters(_signature)\n",
    "parameters\n",
    "\n",
    "actual = _format_symbol_definition(fixture_function, parameters)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5730ad25",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "fixture_function(\n",
      "    arg_1\n",
      ")\n",
      "\n"
     ]
    }
   ],
   "source": [
    "def fixture_function(arg_1: str) -> str:\n",
    "    pass\n",
    "\n",
    "\n",
    "expected = \"\"\"fixture_function(\n",
    "    arg_1\n",
    ")\n",
    "\"\"\"\n",
    "_signature = signature(fixture_function)\n",
    "parameters = _get_parameters(_signature)\n",
    "actual = _format_symbol_definition(fixture_function, parameters)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5a820ac9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "fixture_function(\n",
      "    arg_1, arg_2, arg_3=80\n",
      ")\n",
      "\n"
     ]
    }
   ],
   "source": [
    "def fixture_function(\n",
    "    arg_1: str, *, arg_2, arg_3: Optional[int] = 80\n",
    ") -> str:\n",
    "    pass\n",
    "\n",
    "expected = \"\"\"fixture_function(\n",
    "    arg_1, arg_2, arg_3=80\n",
    ")\n",
    "\"\"\"\n",
    "_signature = signature(fixture_function)\n",
    "parameters = _get_parameters(_signature)\n",
    "actual = _format_symbol_definition(fixture_function, parameters)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eb70e2e9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "fixture_function(\n",
      "    arg_1, arg_2, arg_3=None, arg_4={}, arg_5=(1, 'string', 2.0)\n",
      ")\n",
      "\n"
     ]
    }
   ],
   "source": [
    "def fixture_function(arg_1: str, arg_2, arg_3: Optional[str] = None, arg_4:  Dict[str, int] = {}, arg_5: Tuple[int, str, float] = (1, \"string\", 2.0)) -> str:\n",
    "    pass\n",
    "\n",
    "\n",
    "expected = \"\"\"fixture_function(\n",
    "    arg_1, arg_2, arg_3=None, arg_4={}, arg_5=(1, 'string', 2.0)\n",
    ")\n",
    "\"\"\"\n",
    "_signature = signature(fixture_function)\n",
    "parameters = _get_parameters(_signature)\n",
    "actual = _format_symbol_definition(fixture_function, parameters)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "34d63768",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "fixture_function(\n",
      "    arg_1,\n",
      "    arg_2='default_string',\n",
      "    arg_3={},\n",
      "    arg_4=None,\n",
      "    arg_5=(1, 'string', 2.0),\n",
      "    arg_6=[1, 'string'],\n",
      "    arg_7={1, 2, 3},\n",
      "    arg_8='string',\n",
      ")\n",
      "\n"
     ]
    }
   ],
   "source": [
    "def fixture_function(\n",
    "    arg_1: int,\n",
    "    arg_2: str = \"default_string\",\n",
    "    arg_3: Dict[str, int] = {},\n",
    "    arg_4: Optional[float] = None,\n",
    "    arg_5: Tuple[int, str, float] = (1, \"string\", 2.0),\n",
    "    arg_6: List[Union[int, str]] = [1, \"string\"],\n",
    "    arg_7: Set[int] = {1,2,3},\n",
    "    arg_8: str = \"string\"\n",
    ") -> None:\n",
    "    pass\n",
    "\n",
    "expected = \"\"\"fixture_function(\n",
    "    arg_1,\n",
    "    arg_2='default_string',\n",
    "    arg_3={},\n",
    "    arg_4=None,\n",
    "    arg_5=(1, 'string', 2.0),\n",
    "    arg_6=[1, 'string'],\n",
    "    arg_7={1, 2, 3},\n",
    "    arg_8='string',\n",
    ")\n",
    "\"\"\"\n",
    "\n",
    "_signature = signature(fixture_function)\n",
    "parameters = _get_parameters(_signature)\n",
    "\n",
    "actual = _format_symbol_definition(fixture_function, parameters)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0f960bf1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['/tmp/tmporcyfl70', '/work/fastkafka/nbs', '/usr/lib/python311.zip', '/usr/lib/python3.11', '/usr/lib/python3.11/lib-dynload', '', '/home/harish/.local/lib/python3.11/site-packages', '/work/fastkafka', '/usr/local/lib/python3.11/dist-packages', '/usr/lib/python3/dist-packages']\n"
     ]
    }
   ],
   "source": [
    "@contextmanager\n",
    "def add_tmp_path_to_sys_path(dir_):\n",
    "    dir_ = Path(dir_).absolute().resolve(strict=True)\n",
    "    original_path = sys.path[:]\n",
    "    sys.path.insert(0, str(dir_))\n",
    "    try:\n",
    "        yield\n",
    "    finally:\n",
    "        sys.path = original_path\n",
    "        \n",
    "with TemporaryDirectory() as d:\n",
    "    with add_tmp_path_to_sys_path(d):\n",
    "        actual = sys.path[:]\n",
    "        print(actual)\n",
    "        assert str(Path(d).resolve()) in actual"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ef379f1f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "def _get_exps(mod: str) -> Dict[str, str]:\n",
    "    mf = _find_mod(mod)\n",
    "    if not mf: return {}\n",
    "    txt = mf.read_text()\n",
    "    _def_types = ast.FunctionDef,ast.AsyncFunctionDef,ast.ClassDef\n",
    "    d = {}\n",
    "    for tree in ast.parse(txt).body:\n",
    "        if isinstance(tree, _def_types):\n",
    "            for t in L(patch_name(tree)): d[t] = f\"{tree.lineno}-L{tree.end_lineno}\"\n",
    "        if isinstance(tree, ast.ClassDef): d.update({tree.name+\".\"+t2.name: f\"{t2.lineno}-L{t2.end_lineno}\" for t2 in tree.body if isinstance(t2, _def_types)})\n",
    "    return d"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "00ceebf0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'FixtureClass': '5-L22', 'FixtureClass.__init__': '6-L8', 'FixtureClass.class_method': '11-L13', 'FixtureClass.static_method': '16-L18', 'FixtureClass.instance_method': '20-L22'}\n"
     ]
    }
   ],
   "source": [
    "module_code = '''__all__ = ['FixtureClass']\n",
    "\n",
    "from typing import *\n",
    "\n",
    "class FixtureClass:    \n",
    "    def __init__(self, attribute):\n",
    "        \"\"\"__init__ documentation\"\"\"\n",
    "        self.attribute = attribute\n",
    "    \n",
    "    @classmethod\n",
    "    def class_method(cls):\n",
    "        \"\"\"class_method documentation\"\"\"\n",
    "        return cls.class_variable\n",
    "    \n",
    "    @staticmethod\n",
    "    def static_method():\n",
    "        \"\"\"static_method documentation\"\"\"\n",
    "        return \"This is a static method\"\n",
    "    \n",
    "    def instance_method(self):\n",
    "        \"\"\"instance_method documentation\"\"\"\n",
    "        return \"This is an instance method\"\n",
    "'''\n",
    "\n",
    "with TemporaryDirectory() as d:\n",
    "    my_package = f\"mypackage_{random.randint(0, 1000)}\"\n",
    "    module_name = \"mymodule\"\n",
    "\n",
    "    my_package_path = Path(d) / my_package\n",
    "    my_package_path.mkdir(parents=True)\n",
    "\n",
    "    file_path = my_package_path / f\"{module_name}.py\"\n",
    "\n",
    "    with open(file_path, \"w\", encoding=\"utf-8\") as file:\n",
    "        file.write(module_code)\n",
    "\n",
    "    with open((my_package_path / \"__init__.py\"), \"w\") as f:\n",
    "        f.write('__version__ = \"0.0.1\"')\n",
    "\n",
    "    with add_tmp_path_to_sys_path(d):\n",
    "        actual = _get_exps(f\"{my_package}/{module_name}.py\")\n",
    "        expected = {\n",
    "            \"FixtureClass\": \"5-L22\",\n",
    "            \"FixtureClass.__init__\": \"6-L8\",\n",
    "            \"FixtureClass.class_method\": \"11-L13\",\n",
    "            \"FixtureClass.static_method\": \"16-L18\",\n",
    "            \"FixtureClass.instance_method\": \"20-L22\",\n",
    "        }\n",
    "        print(actual)\n",
    "        assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c5a34e8e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _lineno(sym: str, fname: str) -> Optional[str]:\n",
    "    return _get_exps(fname).get(sym, None) if fname else None\n",
    "\n",
    "\n",
    "@lru_cache(None)\n",
    "class CustomNbdevLookup(NbdevLookup.__wrapped__): # type: ignore\n",
    "    def __init__(\n",
    "        self, strip_libs: Optional[str] = None, incl_libs: Optional[str] = None, skip_mods: Optional[str] = None\n",
    "    ):\n",
    "        super().__init__(strip_libs, incl_libs, skip_mods)\n",
    "\n",
    "    def code(self, sym: str) -> Optional[str]:\n",
    "        \"Link to source code for `sym`\"\n",
    "        res = self[sym]\n",
    "        if not isinstance(res, tuple):\n",
    "            return None\n",
    "        _, py, gh = res\n",
    "        line = _lineno(sym, py)\n",
    "        return f\"{gh}#L{line}\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5c94972e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L177-L427\n"
     ]
    }
   ],
   "source": [
    "actual = CustomNbdevLookup().code('FastKafka')\n",
    "print(actual)\n",
    "\n",
    "pattern = r'#L\\d+-L\\d+'\n",
    "assert re.search(pattern, actual)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4300c760",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\n"
     ]
    }
   ],
   "source": [
    "@contextmanager\n",
    "def mock_custom_nbdev_lookup():\n",
    "    with patch('__main__.CustomNbdevLookup') as MockCustomNbdevLookup:\n",
    "        instance = MockCustomNbdevLookup.return_value\n",
    "        instance.code.return_value = \"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\"\n",
    "        yield\n",
    "\n",
    "with mock_custom_nbdev_lookup():\n",
    "    actual = CustomNbdevLookup().code(\"some_symbol_qualname\")\n",
    "    print(actual)\n",
    "    expected = \"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\"\n",
    "    assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1f1e8563",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _get_symbol_source_link(symbol: Type, lib_version: str) -> str:\n",
    "    \"\"\"Returns the source code link for a given symbol.\n",
    "\n",
    "    Args:\n",
    "        symbol: The symbol to get the source code link for.\n",
    "        lib_version: The current version of the library.\n",
    "\n",
    "    Returns:\n",
    "        The source code link for the symbol.\n",
    "    \"\"\"\n",
    "    symbol = symbol.fget if isinstance(symbol, property) else symbol\n",
    "    source_link = CustomNbdevLookup().code(f\"{symbol.__qualname__}\")\n",
    "    \n",
    "    if source_link is None:\n",
    "        return ''\n",
    "    \n",
    "    href = (\n",
    "        source_link.replace(\"/blob/main/\", f\"/blob/{lib_version}/\")\n",
    "        if lib_version.replace(\".\", \"\").isdigit()\n",
    "        else source_link\n",
    "    )\n",
    "    return f'<a href=\"{href}\" class=\"link-to-source\" target=\"_blank\">View source</a>'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "880e2b1e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.7.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.7.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.7.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n"
     ]
    }
   ],
   "source": [
    "class FixtureClass:\n",
    "    def __init__(self, attribute):\n",
    "        self.attribute = attribute\n",
    "    \n",
    "    @property\n",
    "    def property_attribute(self):\n",
    "        pass\n",
    "    \n",
    "    @classmethod\n",
    "    def class_method(cls):\n",
    "        pass\n",
    "    \n",
    "    def instance_method(self):\n",
    "        pass\n",
    "\n",
    "with mock_custom_nbdev_lookup():\n",
    "    members = getmembers(FixtureClass, lambda a : isfunction(a) or ismethod(a))\n",
    "    lib_version = \"0.7.0\"\n",
    "    for m in members:\n",
    "        actual = _get_symbol_source_link(m[1], lib_version)\n",
    "        print(actual)\n",
    "        expected = '''<a href=\"https://github.com/airtai/fastkafka/blob/0.7.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>'''\n",
    "        assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "453ec902",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n"
     ]
    }
   ],
   "source": [
    "with mock_custom_nbdev_lookup():\n",
    "    members = getmembers(FixtureClass, lambda a : isfunction(a) or ismethod(a))\n",
    "    lib_version = \"0.8.0rc0\"\n",
    "    for m in members:\n",
    "        actual = _get_symbol_source_link(m[1], lib_version)\n",
    "        print(actual)\n",
    "        expected = '''<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>'''\n",
    "        assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d0c27057",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n"
     ]
    }
   ],
   "source": [
    "with mock_custom_nbdev_lookup():\n",
    "    members = getmembers(FixtureClass, lambda a : isfunction(a) or ismethod(a))\n",
    "    lib_version = \"dev\"\n",
    "    for m in members:\n",
    "        actual = _get_symbol_source_link(m[1], lib_version)\n",
    "        print(actual)\n",
    "        expected = '''<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>'''\n",
    "        assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9e14309e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _get_method_type(symbol: Type) -> str:\n",
    "    try:\n",
    "        source = getsource(symbol).strip()\n",
    "    except (TypeError, OSError) as e:\n",
    "        return \"\"\n",
    "\n",
    "    first_line = source.split(\"\\n\")[0]\n",
    "    return (\n",
    "        f\"{first_line}\\n\"\n",
    "        if first_line\n",
    "        in [\"@abstractmethod\", \"@staticmethod\", \"@classmethod\", \"@property\"]\n",
    "        else \"\"\n",
    "    )\n",
    "\n",
    "\n",
    "def _get_symbol_definition(symbol: Type, header_level: int, lib_version: str) -> str:\n",
    "    \"\"\"Return the definition of a given symbol.\n",
    "\n",
    "    Args:\n",
    "        symbol: A function or method object to get the definition for.\n",
    "        header_level: The level of the markdown header to append.\n",
    "        lib_version: The current version of the library.\n",
    "\n",
    "    Returns:\n",
    "        A string representing the function definition\n",
    "    \"\"\"\n",
    "    if isclass(symbol):\n",
    "        return f\"{'#'*(header_level - 1)} {symbol.__module__}.{symbol.__name__} {{#{symbol.__module__}.{symbol.__name__}}}\\n\\n{_get_symbol_source_link(symbol, lib_version)}\\n\\n\"\n",
    "\n",
    "    if isinstance(symbol, property):\n",
    "        symbol = symbol.fget\n",
    "\n",
    "    symbol_anchor = (\n",
    "        f\"{'#' * header_level} {symbol.__name__}\"\n",
    "        + f\" {{#{symbol.__module__}.{'.'.join([component.strip('_') for component in symbol.__qualname__.rsplit('.', 1)])}}}\\n\\n\"\n",
    "    )\n",
    "\n",
    "    link_to_source = f\"{_get_symbol_source_link(symbol, lib_version)}\\n\\n\"\n",
    "\n",
    "    _signature = signature(symbol)\n",
    "    parameters = _get_parameters(_signature)\n",
    "    symbol_definition = f\"```py\\n{_get_method_type(symbol)}{_format_symbol_definition(symbol, parameters)}```\\n\"\n",
    "    return symbol_anchor + link_to_source + symbol_definition"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "649653b0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "#### __main__.MyClass {#__main__.MyClass}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'##### __init__ {#__main__.MyClass.init}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/1.0.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n__init__(\\n    self\\n)\\n```\\n'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "##### class_method {#__main__.MyClass.class_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/1.0.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@classmethod\n",
      "class_method(\n",
      "    a\n",
      ")\n",
      "```\n",
      "\n",
      "##### abstract_method {#__main__.MyClass.abstract_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@abstractmethod\n",
      "abstract_method(\n",
      "    self, xyz\n",
      ")\n",
      "```\n",
      "\n",
      "##### static_method {#__main__.MyClass.static_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@staticmethod\n",
      "static_method(\n",
      "    x\n",
      ")\n",
      "```\n",
      "\n",
      "##### instance_method {#__main__.MyClass.instance_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "instance_method(\n",
      "    self, a, b, c\n",
      ")\n",
      "```\n",
      "\n",
      "##### property_attribute {#__main__.MyClass.property_attribute}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@property\n",
      "property_attribute(\n",
      "    self, a\n",
      ")\n",
      "```\n",
      "\n"
     ]
    }
   ],
   "source": [
    "def fixture_decorator(func):\n",
    "    @functools.wraps(func)\n",
    "    def wrapped_func():\n",
    "        func()\n",
    "    return wrapped_func\n",
    "\n",
    "class MyClass:\n",
    "    attribute = \"Some Attribute\"\n",
    "    \n",
    "    def __init__(self):\n",
    "        \"\"\"__init__ documentation\"\"\"\n",
    "        pass\n",
    "\n",
    "    @fixture_decorator\n",
    "    def instance_method(self, a, b, c):\n",
    "        \"\"\"instance_method documentation\"\"\"\n",
    "        pass\n",
    "\n",
    "    @property\n",
    "    def property_attribute(self, a):\n",
    "        \"\"\"property_attribute documentation\"\"\"\n",
    "        return self.attribute\n",
    "\n",
    "    @classmethod\n",
    "    @fixture_decorator\n",
    "    def class_method(cls, a):\n",
    "        \"\"\"class_method documentation\"\"\"\n",
    "        return cls.class_variable\n",
    "\n",
    "    @staticmethod\n",
    "    def static_method(x):\n",
    "        \"\"\"static_method documentation\"\"\"\n",
    "        pass\n",
    "\n",
    "    @abstractmethod\n",
    "    def abstract_method(self, xyz):\n",
    "        \"\"\"abstract_method documentation\"\"\"\n",
    "        pass\n",
    "\n",
    "\n",
    "with mock_custom_nbdev_lookup():\n",
    "    actual = _get_symbol_definition(MyClass, 5, \"dev\")\n",
    "    print(actual)\n",
    "    assert actual == '#### __main__.MyClass {#__main__.MyClass}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n'\n",
    "    \n",
    "    actual = _get_symbol_definition(MyClass.__init__, 5, \"1.0.0\")\n",
    "    display(actual)\n",
    "    assert (\n",
    "        actual\n",
    "        == '''##### __init__ {#__main__.MyClass.init}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/1.0.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n__init__(\\n    self\\n)\\n```\\n'''\n",
    "    )\n",
    "    \n",
    "    \n",
    "    actual = _get_symbol_definition(MyClass.class_method, 5, \"1.0.0\")\n",
    "    print(actual)\n",
    "    assert (\n",
    "        actual\n",
    "        == '''##### class_method {#__main__.MyClass.class_method}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/1.0.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n@classmethod\\nclass_method(\\n    a\\n)\\n```\\n'''\n",
    "    )\n",
    "\n",
    "    actual = _get_symbol_definition(MyClass.abstract_method, 5, \"0.2.0rc0\")\n",
    "    print(actual)\n",
    "    assert (\n",
    "        actual\n",
    "        == '''##### abstract_method {#__main__.MyClass.abstract_method}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n@abstractmethod\\nabstract_method(\\n    self, xyz\\n)\\n```\\n'''\n",
    "    )\n",
    "\n",
    "    actual = _get_symbol_definition(MyClass.static_method, 5, \"0.7.0dev\")\n",
    "    print(actual)\n",
    "    assert (\n",
    "        actual\n",
    "        == '''##### static_method {#__main__.MyClass.static_method}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n@staticmethod\\nstatic_method(\\n    x\\n)\\n```\\n'''\n",
    "    )\n",
    "\n",
    "    actual = _get_symbol_definition(MyClass.instance_method, 5, \"1.0.1-rc.0.1\")\n",
    "    print(actual)\n",
    "    assert (\n",
    "        actual\n",
    "        == '''##### instance_method {#__main__.MyClass.instance_method}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\ninstance_method(\\n    self, a, b, c\\n)\\n```\\n'''\n",
    "    )\n",
    "\n",
    "    actual = _get_symbol_definition(MyClass.property_attribute, 5, \"1.0\")\n",
    "    print(actual)\n",
    "    assert (\n",
    "        actual\n",
    "        == '''##### property_attribute {#__main__.MyClass.property_attribute}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n@property\\nproperty_attribute(\\n    self, a\\n)\\n```\\n'''\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "55f76f70",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "##### fixture_function {#__main__.fixture_function}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.7.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "fixture_function()\n",
      "```\n",
      "\n"
     ]
    }
   ],
   "source": [
    "def fixture_function() -> None:\n",
    "    pass\n",
    "\n",
    "with mock_custom_nbdev_lookup():\n",
    "    actual = _get_symbol_definition(fixture_function, 5, \"0.7.0\")\n",
    "    expected = \"\"\"    ##### fixture_function {#__main__.fixture_function}\n",
    "\n",
    "    <a href=\"https://github.com/airtai/fastkafka/blob/0.7.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
    "\n",
    "    ```py\n",
    "    fixture_function()\n",
    "    ```\n",
    "    \"\"\"\n",
    "\n",
    "    print(actual)\n",
    "    assert actual == textwrap.dedent(expected)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a667280a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "### fixture_function {#__main__.fixture_function}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "fixture_function(\n",
      "    arg_1, arg_2\n",
      ")\n",
      "```\n",
      "\n"
     ]
    }
   ],
   "source": [
    "TestCallable = Callable[[BaseModel], Union[Awaitable[None], None]]\n",
    "\n",
    "def fixture_function(arg_1: str, arg_2) -> TestCallable:\n",
    "    pass\n",
    "\n",
    "\n",
    "with mock_custom_nbdev_lookup():\n",
    "    actual = _get_symbol_definition(fixture_function, 3, \"1.0\")\n",
    "    expected = \"\"\"    ### fixture_function {#__main__.fixture_function}\n",
    "    \n",
    "    <a href=\"https://github.com/airtai/fastkafka/blob/1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
    "\n",
    "    ```py\n",
    "    fixture_function(\n",
    "        arg_1, arg_2\n",
    "    )\n",
    "    ```\n",
    "    \"\"\"\n",
    "\n",
    "    print(actual)\n",
    "    assert actual == textwrap.dedent(expected)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2e084d12",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "### fixture_function {#__main__.fixture_function}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "fixture_function(\n",
      "    api_version='auto',\n",
      "    acks=<object object>,\n",
      "    max_poll_interval_ms=300000,\n",
      "    partition_assignment_strategy=(<class 'kafka.coordinator.assignors.roundrobin.RoundRobinPartitionAssignor'>,),\n",
      "    isolation_level='read_uncommitted',\n",
      ")\n",
      "```\n",
      "\n"
     ]
    }
   ],
   "source": [
    "_object = object()\n",
    "\n",
    "def fixture_function(\n",
    "    api_version=\"auto\",\n",
    "    acks=_object,\n",
    "    max_poll_interval_ms=300000,\n",
    "    partition_assignment_strategy=(RoundRobinPartitionAssignor,),\n",
    "    isolation_level=\"read_uncommitted\") -> None:\n",
    "    pass\n",
    "\n",
    "with mock_custom_nbdev_lookup():\n",
    "\n",
    "    actual = _get_symbol_definition(fixture_function, 3, \"dev\")\n",
    "    print(actual)\n",
    "    assert actual is not None\n",
    "    assert '''<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>''' in actual"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "184e1deb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "# fixture_function {#__main__.fixture_function}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "fixture_function(\n",
      "    arg_1,\n",
      "    arg_2='default_string',\n",
      "    arg_3={},\n",
      "    arg_4=None,\n",
      "    arg_5=(1, 'string', 2.0),\n",
      "    arg_6=[1, 'string'],\n",
      "    arg_7={1, 2, 3},\n",
      "    arg_8='string',\n",
      ")\n",
      "```\n",
      "\n"
     ]
    }
   ],
   "source": [
    "def fixture_function(\n",
    "    arg_1: int,\n",
    "    arg_2: str = \"default_string\",\n",
    "    arg_3: Dict[str, int] = {},\n",
    "    arg_4: Optional[float] = None,\n",
    "    arg_5: Tuple[int, str, float] = (1, \"string\", 2.0),\n",
    "    arg_6: List[Union[int, str]] = [1, \"string\"],\n",
    "    arg_7: Set[int] = {1, 2, 3},\n",
    "    arg_8: Union[int, str] = \"string\",\n",
    ") -> None:\n",
    "    pass\n",
    "\n",
    "with mock_custom_nbdev_lookup():\n",
    "    actual = _get_symbol_definition(fixture_function, 1, \"1.0\")\n",
    "    expected = \"\"\"    # fixture_function {#__main__.fixture_function}\n",
    "    \n",
    "    <a href=\"https://github.com/airtai/fastkafka/blob/1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
    "\n",
    "    ```py\n",
    "    fixture_function(\n",
    "        arg_1,\n",
    "        arg_2='default_string',\n",
    "        arg_3={},\n",
    "        arg_4=None,\n",
    "        arg_5=(1, 'string', 2.0),\n",
    "        arg_6=[1, 'string'],\n",
    "        arg_7={1, 2, 3},\n",
    "        arg_8='string',\n",
    "    )\n",
    "    ```\n",
    "    \"\"\"\n",
    "\n",
    "    print(actual)\n",
    "    assert actual == textwrap.dedent(expected)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4e19a478",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _is_method(symbol: Type) -> bool:\n",
    "    \"\"\"Check if the given symbol is a method.\n",
    "\n",
    "    Args:\n",
    "        symbol: A function or method object to check.\n",
    "\n",
    "    Returns:\n",
    "        A boolean indicating whether the symbol is a method.\n",
    "    \"\"\"\n",
    "    return (\n",
    "        ismethod(symbol)\n",
    "        or isfunction(symbol)\n",
    "        or isinstance(symbol, property)\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "55e4666d",
   "metadata": {},
   "outputs": [],
   "source": [
    "assert _is_method(MyClass.instance_method)\n",
    "assert _is_method(MyClass.static_method)\n",
    "assert _is_method(MyClass.class_method)\n",
    "assert _is_method(MyClass.abstract_method)\n",
    "assert _is_method(MyClass.property_attribute)\n",
    "assert not _is_method(MyClass.attribute)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "60e81719",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "def _get_formatted_docstring_for_symbol(\n",
    "    symbol: Type,\n",
    "    lib_version: str,\n",
    "    header_level: int = 2\n",
    ") -> str:\n",
    "    \"\"\"Recursively parses and get formatted docstring of a symbol.\n",
    "\n",
    "    Args:\n",
    "        symbol: A Python class or function object to parse the docstring for.\n",
    "        lib_version: The current version of the library.\n",
    "        header_level: The level of the markdown header to append.\n",
    "\n",
    "    Returns:\n",
    "        A formatted docstring of the symbol and its members.\n",
    "\n",
    "    \"\"\"\n",
    "    def traverse(symbol: Type, contents: str, header_level: int, lib_version: str) -> str:\n",
    "        \"\"\"Recursively traverse the members of a symbol and append their docstrings to the provided contents string.\n",
    "\n",
    "        Args:\n",
    "            symbol: A Python class or function object to parse the docstring for.\n",
    "            contents: The current formatted docstrings.\n",
    "            header_level: The level of the markdown header to append.\n",
    "            lib_version: The current version of the library.\n",
    "\n",
    "        Returns:\n",
    "            The updated formatted docstrings.\n",
    "\n",
    "        \"\"\"\n",
    "        for x, y in getmembers(symbol):\n",
    "            if not x.startswith(\"_\") or x == \"__init__\":\n",
    "                if _is_method(y):\n",
    "                    contents += f\"{_get_symbol_definition(y, header_level, lib_version)}\\n{_docstring_to_markdown(y)}\"\n",
    "                elif isclass(y) and not x.startswith(\"_\"):\n",
    "                    contents += f\"{_get_symbol_definition(y, header_level+1, lib_version)}\\n{_docstring_to_markdown(y)}\"\n",
    "                    contents = traverse(y, contents, header_level+1, lib_version)\n",
    "        return contents\n",
    "\n",
    "    contents = f\"{_get_symbol_definition(symbol, header_level+1, lib_version)}\\n{_docstring_to_markdown(symbol)}\"\n",
    "    if isclass(symbol):\n",
    "        contents = traverse(symbol, contents, header_level+1, lib_version)\n",
    "    return contents"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "28dfdda9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "## __main__.FixtureClass {#__main__.FixtureClass}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "\n",
      "### __init__ {#__main__.FixtureClass.init}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "__init__(\n",
      "    self, attribute\n",
      ")\n",
      "```\n",
      "\n",
      "### abstract_method {#__main__.FixtureClass.abstract_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@abstractmethod\n",
      "abstract_method(\n",
      "    self\n",
      ")\n",
      "```\n",
      "\n",
      "### class_method {#__main__.FixtureClass.class_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@classmethod\n",
      "class_method()\n",
      "```\n",
      "\n",
      "### instance_method {#__main__.FixtureClass.instance_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "instance_method(\n",
      "    self\n",
      ")\n",
      "```\n",
      "\n",
      "### property_attribute {#__main__.FixtureClass.property_attribute}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@property\n",
      "property_attribute(\n",
      "    self\n",
      ")\n",
      "```\n",
      "\n",
      "### static_method {#__main__.FixtureClass.static_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@staticmethod\n",
      "static_method()\n",
      "```\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "class FixtureClass:\n",
    "    def __init__(self, attribute):\n",
    "        self.attribute = attribute\n",
    "        \n",
    "    @property\n",
    "    def property_attribute(self):\n",
    "        pass\n",
    "    \n",
    "    @classmethod\n",
    "    def class_method(cls):\n",
    "        pass\n",
    "    \n",
    "    @staticmethod\n",
    "    def static_method():\n",
    "        pass\n",
    "    \n",
    "    def instance_method(self):\n",
    "        pass\n",
    "    \n",
    "    @abstractmethod\n",
    "    def abstract_method(self):\n",
    "        pass\n",
    "    \n",
    "with mock_custom_nbdev_lookup():\n",
    "    actual = _get_formatted_docstring_for_symbol(FixtureClass, \"0.1.0\")\n",
    "    expected = '''## __main__.FixtureClass {#__main__.FixtureClass}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n\\n### __init__ {#__main__.FixtureClass.init}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n__init__(\\n    self, attribute\\n)\\n```\\n\\n### abstract_method {#__main__.FixtureClass.abstract_method}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n@abstractmethod\\nabstract_method(\\n    self\\n)\\n```\\n\\n### class_method {#__main__.FixtureClass.class_method}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n@classmethod\\nclass_method()\\n```\\n\\n### instance_method {#__main__.FixtureClass.instance_method}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\ninstance_method(\\n    self\\n)\\n```\\n\\n### property_attribute {#__main__.FixtureClass.property_attribute}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n@property\\nproperty_attribute(\\n    self\\n)\\n```\\n\\n### static_method {#__main__.FixtureClass.static_method}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n@staticmethod\\nstatic_method()\\n```\\n\\n'''\n",
    "    print(actual)\n",
    "    \n",
    "    assert actual == expected, actual"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8aa4c21e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "## __main__.FixtureClass {#__main__.FixtureClass}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "\n",
      "### __main__.NestedClass {#__main__.NestedClass}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "\n",
      "NestedClass documentation\n",
      "\n",
      "#### __main__.NestedNestedClass {#__main__.NestedNestedClass}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "\n",
      "NestedNestedClass documentation\n",
      "\n",
      "##### nested_nested_method {#__main__.FixtureClass.NestedClass.NestedNestedClass.nested_nested_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "nested_nested_method(\n",
      "    self\n",
      ")\n",
      "```\n",
      "\n",
      "nested_nested_method documentation\n",
      "\n",
      "#### nested_method {#__main__.FixtureClass.NestedClass.nested_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "nested_method(\n",
      "    self\n",
      ")\n",
      "```\n",
      "\n",
      "nested_method documentation\n",
      "\n",
      "### __init__ {#__main__.FixtureClass.init}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "__init__(\n",
      "    self, attribute\n",
      ")\n",
      "```\n",
      "\n",
      "__init__ documentation\n",
      "\n",
      "### abstract_method {#__main__.FixtureClass.abstract_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@abstractmethod\n",
      "abstract_method(\n",
      "    self\n",
      ")\n",
      "```\n",
      "\n",
      "abstract_method documentation\n",
      "\n",
      "### class_method {#__main__.FixtureClass.class_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@classmethod\n",
      "class_method()\n",
      "```\n",
      "\n",
      "class_method documentation\n",
      "\n",
      "### instance_method {#__main__.FixtureClass.instance_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "instance_method(\n",
      "    self\n",
      ")\n",
      "```\n",
      "\n",
      "instance_method documentation\n",
      "\n",
      "### property_attribute {#__main__.FixtureClass.property_attribute}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@property\n",
      "property_attribute(\n",
      "    self\n",
      ")\n",
      "```\n",
      "\n",
      "property_attribute documentation\n",
      "\n",
      "### static_method {#__main__.FixtureClass.static_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@staticmethod\n",
      "static_method()\n",
      "```\n",
      "\n",
      "static_method documentation\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "class FixtureClass:\n",
    "    class_variable = 10\n",
    "    \n",
    "    def __init__(self, attribute):\n",
    "        \"\"\"__init__ documentation\"\"\"\n",
    "        self.attribute = attribute\n",
    "    \n",
    "    @property\n",
    "    def property_attribute(self):\n",
    "        \"\"\"property_attribute documentation\"\"\"\n",
    "        return self.attribute\n",
    "    \n",
    "    @classmethod\n",
    "    def class_method(cls):\n",
    "        \"\"\"class_method documentation\"\"\"\n",
    "        return cls.class_variable\n",
    "    \n",
    "    @staticmethod\n",
    "    @fixture_decorator\n",
    "    def static_method():\n",
    "        \"\"\"static_method documentation\"\"\"\n",
    "        return \"This is a static method\"\n",
    "    \n",
    "    @fixture_decorator\n",
    "    def instance_method(self):\n",
    "        \"\"\"instance_method documentation\"\"\"\n",
    "        return \"This is an instance method\"\n",
    "    \n",
    "    def __str__(self):\n",
    "        \"\"\"__str__ documentation\"\"\"\n",
    "        return f\"MyClass instance with attribute: {self.attribute}\"\n",
    "    \n",
    "    @abstractmethod\n",
    "    def abstract_method(self):\n",
    "        \"\"\"abstract_method documentation\"\"\"\n",
    "        pass\n",
    "    \n",
    "    class NestedClass:\n",
    "        \"\"\"NestedClass documentation\"\"\"\n",
    "        def nested_method(self):\n",
    "            \"\"\"nested_method documentation\"\"\"\n",
    "            return \"This is a method in the nested class\"\n",
    "        \n",
    "        class NestedNestedClass:\n",
    "            \"\"\"NestedNestedClass documentation\"\"\"\n",
    "            \n",
    "            def nested_nested_method(self):\n",
    "                \"\"\"nested_nested_method documentation\"\"\"\n",
    "                return \"This is a method in the nested_nested class\"\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "with mock_custom_nbdev_lookup():\n",
    "    actual = _get_formatted_docstring_for_symbol(FixtureClass, \"0.1.0\")\n",
    "    expected = '''## __main__.FixtureClass {#__main__.FixtureClass}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n\\n### __main__.NestedClass {#__main__.NestedClass}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n\\nNestedClass documentation\\n\\n#### __main__.NestedNestedClass {#__main__.NestedNestedClass}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n\\nNestedNestedClass documentation\\n\\n##### nested_nested_method {#__main__.FixtureClass.NestedClass.NestedNestedClass.nested_nested_method}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\nnested_nested_method(\\n    self\\n)\\n```\\n\\nnested_nested_method documentation\\n\\n#### nested_method {#__main__.FixtureClass.NestedClass.nested_method}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\nnested_method(\\n    self\\n)\\n```\\n\\nnested_method documentation\\n\\n### __init__ {#__main__.FixtureClass.init}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n__init__(\\n    self, attribute\\n)\\n```\\n\\n__init__ documentation\\n\\n### abstract_method {#__main__.FixtureClass.abstract_method}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n@abstractmethod\\nabstract_method(\\n    self\\n)\\n```\\n\\nabstract_method documentation\\n\\n### class_method {#__main__.FixtureClass.class_method}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n@classmethod\\nclass_method()\\n```\\n\\nclass_method documentation\\n\\n### instance_method {#__main__.FixtureClass.instance_method}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\ninstance_method(\\n    self\\n)\\n```\\n\\ninstance_method documentation\\n\\n### property_attribute {#__main__.FixtureClass.property_attribute}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n@property\\nproperty_attribute(\\n    self\\n)\\n```\\n\\nproperty_attribute documentation\\n\\n### static_method {#__main__.FixtureClass.static_method}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n@staticmethod\\nstatic_method()\\n```\\n\\nstatic_method documentation\\n\\n'''\n",
    "    print(actual)\n",
    "    \n",
    "    assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bb2ae51d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "### fixture_function {#__main__.fixture_function}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "fixture_function(\n",
      "    arg_1, arg_2, arg_3, arg_4=None\n",
      ")\n",
      "```\n",
      "\n",
      "This is a one line description for the function\n",
      "\n",
      "**Parameters**:\n",
      "\n",
      "|  Name | Type | Description | Default |\n",
      "|---|---|---|---|\n",
      "| `arg_1` | `str` | Argument 1 | *required* |\n",
      "| `arg_2` | `Union[List[str], str]` | Argument 2 | *required* |\n",
      "| `arg_3` | `Optional[int]` | Argument 3 | *required* |\n",
      "| `arg_4` | `Optional[str]` | Argument 4 | `None` |\n",
      "\n",
      "**Returns**:\n",
      "\n",
      "|  Type | Description |\n",
      "|---|---|\n",
      "| `str` | The concatinated string |\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "def fixture_function(\n",
    "    arg_1: str,\n",
    "    arg_2: Union[List[str], str],\n",
    "    arg_3: Optional[int],\n",
    "    arg_4: Optional[str] = None,\n",
    ") -> str:\n",
    "    \"\"\"This is a one line description for the function\n",
    "\n",
    "    Args:\n",
    "        arg_1: Argument 1\n",
    "        arg_2: Argument 2\n",
    "        arg_3: Argument 3\n",
    "        arg_4: Argument 4\n",
    "\n",
    "    Returns:\n",
    "        The concatinated string\n",
    "    \"\"\"\n",
    "    pass\n",
    "\n",
    "\n",
    "_optional_str_type = \"Union[str, NoneType]\" if f\"{sys.version_info.major}.{sys.version_info.minor}\" == \"3.8\" else \"Optional[str]\"\n",
    "_optional_int_type = \"Union[int, NoneType]\" if f\"{sys.version_info.major}.{sys.version_info.minor}\" == \"3.8\" else \"Optional[int]\"\n",
    "expected = \"### fixture_function {#__main__.fixture_function}\"+f\"\"\"\n",
    "\n",
    "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
    "\n",
    "```py\n",
    "fixture_function(\n",
    "    arg_1, arg_2, arg_3, arg_4=None\n",
    ")\n",
    "```\n",
    "\n",
    "This is a one line description for the function\n",
    "\n",
    "**Parameters**:\n",
    "\n",
    "|  Name | Type | Description | Default |\n",
    "|---|---|---|---|\n",
    "| `arg_1` | `str` | Argument 1 | *required* |\n",
    "| `arg_2` | `Union[List[str], str]` | Argument 2 | *required* |\n",
    "| `arg_3` | `{_optional_int_type}` | Argument 3 | *required* |\n",
    "| `arg_4` | `{_optional_str_type}` | Argument 4 | `None` |\n",
    "\n",
    "**Returns**:\n",
    "\n",
    "|  Type | Description |\n",
    "|---|---|\n",
    "| `str` | The concatinated string |\n",
    "\n",
    "\"\"\"\n",
    "\n",
    "\n",
    "with mock_custom_nbdev_lookup():\n",
    "    actual = _get_formatted_docstring_for_symbol(fixture_function, \"0.1.1rc0\")\n",
    "    print(actual)\n",
    "\n",
    "    assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "949b3aa3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "### fixture_function {#__main__.fixture_function}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "fixture_function(\n",
      "    arg_1, arg_2, arg_3=12\n",
      ")\n",
      "```\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "def fixture_function(\n",
    "    arg_1: str,\n",
    "    arg_2: Union[List[str], str],\n",
    "    arg_3: Optional[int] = 12,\n",
    ") -> str:\n",
    "    pass\n",
    "\n",
    "expected = \"### fixture_function {#__main__.fixture_function}\"+f\"\"\"\n",
    "\n",
    "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
    "\n",
    "```py\n",
    "fixture_function(\n",
    "    arg_1, arg_2, arg_3=12\n",
    ")\n",
    "```\n",
    "\n",
    "\"\"\"\n",
    "\n",
    "\n",
    "with mock_custom_nbdev_lookup():\n",
    "    actual = _get_formatted_docstring_for_symbol(fixture_function, \"0.1.1rc0\")\n",
    "    print(actual)\n",
    "\n",
    "    assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ae47ed11",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _convert_html_style_attribute_to_jsx(contents: str) -> str:\n",
    "    \"\"\"Converts the inline style attributes in an HTML string to JSX compatible format.\n",
    "\n",
    "    Args:\n",
    "        contents: A string containing an HTML document or fragment.\n",
    "\n",
    "    Returns:\n",
    "        A string with inline style attributes converted to JSX compatible format.\n",
    "    \"\"\"\n",
    "    style_regex = re.compile(r'style=\"(.+?)\"')\n",
    "    style_matches = style_regex.findall(contents)\n",
    "\n",
    "    for style_match in style_matches:\n",
    "        style_dict = {}\n",
    "        styles = style_match.split(\";\")\n",
    "        for style in styles:\n",
    "            key_value = style.split(\":\")\n",
    "            if len(key_value) == 2:\n",
    "                key = re.sub(\n",
    "                    r\"-(.)\", lambda m: m.group(1).upper(), key_value[0].strip()\n",
    "                )\n",
    "                value = key_value[1].strip().replace(\"'\", '\"')\n",
    "                style_dict[key] = value\n",
    "        replacement = \"style={{\"\n",
    "        for key, value in style_dict.items():\n",
    "            replacement += f\"{key}: '{value}', \"\n",
    "        replacement = replacement[:-2] + \"}}\"\n",
    "        contents = contents.replace(f'style=\"{style_match}\"', replacement)\n",
    "\n",
    "    return contents"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bcaa9e96",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "************************************************************************************************************************\n",
      "<a\n",
      "href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_components/test_dependencies.py#L28\"\n",
      "target=\"_blank\" style={{float: 'right', fontSize: 'smaller'}}>source</a> some text goes here <a\n",
      "href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_components/test_dependencies.py#L28\"\n",
      "target=\"_blank\" style={{float: 'right', fontSize: 'smaller'}}>source</a>\n",
      "************************************************************************************************************************\n",
      "<span style={{color: 'red'}}>Test</span>\n",
      "************************************************************************************************************************\n",
      "<div style={{backgroundColor: 'blue', border: '1px solid black'}}>Test</div>\n",
      "************************************************************************************************************************\n",
      "<span style={{fontSize: '1.2rem'}}>Test</span>\n",
      "************************************************************************************************************************\n",
      "<pre style={{whiteSpace: 'pre', overflowX: 'auto', lineHeight: 'normal', fontFamily: 'Menlo,\"DejaVu Sans Mono\",consolas,\"Courier New\",monospace'}}></pre>\n"
     ]
    }
   ],
   "source": [
    "fixtures = [\n",
    "    {\n",
    "        \"input\": \"\"\"<a\n",
    "href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_components/test_dependencies.py#L28\"\n",
    "target=\"_blank\" style=\"float:right; font-size:smaller\">source</a> some text goes here <a\n",
    "href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_components/test_dependencies.py#L28\"\n",
    "target=\"_blank\" style=\"float:right; font-size:smaller\">source</a>\"\"\",\n",
    "        \"expected\": \"\"\"<a\n",
    "href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_components/test_dependencies.py#L28\"\n",
    "target=\"_blank\" style={{float: 'right', fontSize: 'smaller'}}>source</a> some text goes here <a\n",
    "href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_components/test_dependencies.py#L28\"\n",
    "target=\"_blank\" style={{float: 'right', fontSize: 'smaller'}}>source</a>\"\"\",\n",
    "    },\n",
    "    {\n",
    "        \"input\": '<span style=\"color: red;\">Test</span>',\n",
    "        \"expected\": \"<span style={{color: 'red'}}>Test</span>\",\n",
    "    },\n",
    "    {\n",
    "        \"input\": '<div style=\"background-color: blue; border: 1px solid black;\">Test</div>',\n",
    "        \"expected\": \"<div style={{backgroundColor: 'blue', border: '1px solid black'}}>Test</div>\",\n",
    "    },\n",
    "    {\n",
    "        \"input\": '<span style=\"font-size: 1.2rem;\">Test</span>',\n",
    "        \"expected\": \"<span style={{fontSize: '1.2rem'}}>Test</span>\",\n",
    "    },\n",
    "    {\n",
    "        \"input\": \"\"\"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"></pre>\"\"\",\n",
    "        \"expected\": \"\"\"<pre style={{whiteSpace: 'pre', overflowX: 'auto', lineHeight: 'normal', fontFamily: 'Menlo,\"DejaVu Sans Mono\",consolas,\"Courier New\",monospace'}}></pre>\"\"\",\n",
    "    },\n",
    "]\n",
    "\n",
    "for fixture in fixtures:\n",
    "    actual = _convert_html_style_attribute_to_jsx(fixture[\"input\"])\n",
    "    print(\"*\" * 120)\n",
    "    print(actual)\n",
    "    assert actual == fixture[\"expected\"], fixture[\"expected\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7c8fa05a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _get_all_markdown_files_path(docs_path: Path) -> List[Path]:\n",
    "    \"\"\"Get all Markdown files in a directory and its subdirectories.\n",
    "\n",
    "    Args:\n",
    "        directory: The path to the directory to search in.\n",
    "\n",
    "    Returns:\n",
    "        A list of paths to all Markdown files found in the directory and its subdirectories.\n",
    "    \"\"\"\n",
    "    markdown_files = [file_path for file_path in docs_path.glob(\"**/*.md\")]\n",
    "    return markdown_files"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dc72d20f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[Path('/tmp/tmpbyeo8sb_/docusaurus/docs/file.md'), Path('/tmp/tmpbyeo8sb_/docusaurus/docs/api/file.md'), Path('/tmp/tmpbyeo8sb_/docusaurus/docs/api/fastKafka/file.md'), Path('/tmp/tmpbyeo8sb_/docusaurus/docs/blog/file.md')]\n"
     ]
    }
   ],
   "source": [
    "with TemporaryDirectory() as d:\n",
    "    module_name = \"fastkafka\"\n",
    "\n",
    "    docs_path = Path(d) / \"docusaurus\" / \"docs\"\n",
    "    docs_path.mkdir(parents=True)\n",
    "\n",
    "    api_path = docs_path / \"api\"\n",
    "    api_path.mkdir(parents=True)\n",
    "\n",
    "    blog_path = docs_path / \"blog\"\n",
    "    blog_path.mkdir(parents=True)\n",
    "\n",
    "    nested_api_path = api_path / \"fastKafka\"\n",
    "    nested_api_path.mkdir(parents=True)\n",
    "\n",
    "    for p in [docs_path, api_path, blog_path, nested_api_path]:\n",
    "        with open((p / \"file.md\"), \"w\") as f:\n",
    "            f.write(\"sample text\")\n",
    "\n",
    "    actual = _get_all_markdown_files_path(docs_path)\n",
    "    expected = [\n",
    "        Path(docs_path) / \"file.md\",\n",
    "        Path(api_path) / \"file.md\",\n",
    "        Path(nested_api_path) / \"file.md\",\n",
    "        Path(blog_path) / \"file.md\",\n",
    "    ]\n",
    "\n",
    "    print(actual)\n",
    "    assert sorted(actual) == sorted(expected), expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2bc75616",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _fix_special_symbols_in_html(contents: str) -> str:\n",
    "    contents = contents.replace(\"”\", '\"')\n",
    "    return contents"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dd675ce3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<a href=\"https://colab.research.google.com/github/airtai/fastkafka/blob/main/nbs/guides/Guide_00_FastKafka_Demo.ipynb\" target=\"_blank\">\n"
     ]
    }
   ],
   "source": [
    "fixture = \"\"\"<a href=\"https://colab.research.google.com/github/airtai/fastkafka/blob/main/nbs/guides/Guide_00_FastKafka_Demo.ipynb\" target=”_blank”>\"\"\"\n",
    "expected = \"\"\"<a href=\"https://colab.research.google.com/github/airtai/fastkafka/blob/main/nbs/guides/Guide_00_FastKafka_Demo.ipynb\" target=\"_blank\">\"\"\"\n",
    "\n",
    "actual = _fix_special_symbols_in_html(fixture)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "68923a9c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "def _add_file_extension_to_link(url: str) -> str:\n",
    "    \"\"\"Add file extension to the last segment of a URL\n",
    "\n",
    "    Args:\n",
    "        url: A URL string.\n",
    "\n",
    "    Returns:\n",
    "        A string of the updated URL with a file extension added to the last segment of the URL.\n",
    "    \"\"\"\n",
    "    segments = url.split(\"/#\")[0].split(\"/\")[-2:]\n",
    "    return url.replace(f\"/{segments[1]}\", f\"/{segments[1]}.md\").replace(\".md/#\", \".md#\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "00195964",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "https://airtai.github.io/fastkafka/api/fastkafka/FastKafka.md#fastkafka.FastKafka\n"
     ]
    }
   ],
   "source": [
    "fixture = \"https://airtai.github.io/fastkafka/api/fastkafka/FastKafka/#fastkafka.FastKafka\"\n",
    "expected = \"https://airtai.github.io/fastkafka/api/fastkafka/FastKafka.md#fastkafka.FastKafka\"\n",
    "\n",
    "actual = _add_file_extension_to_link(fixture)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4afbe422",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "https://airtai.github.io/fastkafka/api/fastkafka/testing/ApacheKafkaBroker.md#fastkafka.testing.ApacheKafkaBroker\n"
     ]
    }
   ],
   "source": [
    "fixture = \"https://airtai.github.io/fastkafka/api/fastkafka/testing/ApacheKafkaBroker/#fastkafka.testing.ApacheKafkaBroker\"\n",
    "expected = \"https://airtai.github.io/fastkafka/api/fastkafka/testing/ApacheKafkaBroker.md#fastkafka.testing.ApacheKafkaBroker\"\n",
    "\n",
    "actual = _add_file_extension_to_link(fixture)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c5540873",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "https://github.com/airtai/sample_fastkafka_with_redpanda.md\n"
     ]
    }
   ],
   "source": [
    "fixture = \"https://github.com/airtai/sample_fastkafka_with_redpanda\"\n",
    "expected = \"https://github.com/airtai/sample_fastkafka_with_redpanda.md\"\n",
    "\n",
    "actual = _add_file_extension_to_link(fixture)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b40ecb47",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "def _generate_production_url(url: str) -> str:\n",
    "    \"\"\"Generate a Docusaurus compatible production URL for the given symbol URL.\n",
    "\n",
    "    Args:\n",
    "        url: The symbol URL to be converted.\n",
    "\n",
    "    Returns:\n",
    "        The production URL of the symbol.\n",
    "    \"\"\"\n",
    "    url_segment, hash_segment = url.split(\".md\")\n",
    "    url_split = url_segment.split(\"/\")\n",
    "    if url_split[-1].lower() == url_split[-2].lower():\n",
    "        return \"/\".join(url_split[:-1]) + hash_segment\n",
    "    return url.replace(\".md\", \"\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5b3bb73a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "https://airtai.github.io/fastkafka/docs/api/fastkafka#fastkafka.FastKafka\n",
      "https://airtai.github.io/fastkafka/docs/api/fastkafka/testing/Tester#fastkafka.testing.Tester\n"
     ]
    }
   ],
   "source": [
    "url = \"https://airtai.github.io/fastkafka/docs/api/fastkafka/FastKafka.md#fastkafka.FastKafka\"\n",
    "expected = \"https://airtai.github.io/fastkafka/docs/api/fastkafka#fastkafka.FastKafka\"\n",
    "\n",
    "actual = _generate_production_url(url)\n",
    "print(actual)\n",
    "assert actual == expected\n",
    "\n",
    "expected = \"https://airtai.github.io/fastkafka/docs/api/fastkafka/testing/Tester#fastkafka.testing.Tester\"\n",
    "actual = _generate_production_url(\"https://airtai.github.io/fastkafka/docs/api/fastkafka/testing/Tester.md#fastkafka.testing.Tester\")\n",
    "print(actual)\n",
    "assert actual == expected, actual"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "82d131ac",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _fix_symbol_links(\n",
    "    contents: str,\n",
    "    dir_prefix: str,\n",
    "    doc_host: str,\n",
    "    doc_baseurl: str,\n",
    "    use_relative_doc_links: bool = True,\n",
    ") -> str:\n",
    "    \"\"\"Fix symbol links in Markdown content.\n",
    "\n",
    "    Args:\n",
    "        contents: The Markdown content to search for symbol links.\n",
    "        dir_prefix: Directory prefix to append in the relative URL.\n",
    "        doc_host: The host URL for the documentation site.\n",
    "        doc_baseurl: The base URL for the documentation site.\n",
    "        use_relative_doc_links: If set to True, then the relative link to symbols will be added else,\n",
    "            production link will be added.\n",
    "\n",
    "    Returns:\n",
    "        str: The Markdown content with updated symbol links.\n",
    "    \"\"\"\n",
    "    prefix = re.escape(urljoin(doc_host + \"/\", doc_baseurl))\n",
    "    pattern = re.compile(rf\"\\[(.*?)\\]\\(({prefix}[^)]+)\\)\")\n",
    "    matches = pattern.findall(contents)\n",
    "    for match in matches:\n",
    "        old_url = match[1]\n",
    "        new_url = _add_file_extension_to_link(old_url).replace(\"/api/\", \"/docs/api/\")\n",
    "        if use_relative_doc_links:\n",
    "            dir_prefix = \"./\" if dir_prefix == \"\" else dir_prefix\n",
    "            updated_url = dir_prefix + new_url.split(\"/docs/\")[1]\n",
    "        else:\n",
    "            updated_url = _generate_production_url(\n",
    "                doc_host + doc_baseurl + \"/docs/\" + new_url.split(\"/docs/\")[1]\n",
    "            )\n",
    "        contents = contents.replace(old_url, updated_url)\n",
    "    return contents"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6d45779c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "In the above example,\n",
      "[`FastKafka`](https://airtai.github.io/fastkafka/docs/api/fastkafka#fastkafka.FastKafka)\n",
      "[`FastKafka`](https://airtai.github.io/fastkafka/docs/api/fastkafka#fastkafka.FastKafka)\n",
      "app is named as `kafka_app`\n",
      "[`FastKafka`](https://airtai.github.io/fastkafka/docs/api/fastkafka#fastkafka.FastKafka)\n"
     ]
    }
   ],
   "source": [
    "fixture = \"\"\"In the above example,\n",
    "[`FastKafka`](https://airtai.github.io/fastkafka/api/fastkafka/FastKafka/#fastkafka.FastKafka)\n",
    "[`FastKafka`](https://airtai.github.io/fastkafka/0.5.0/api/fastkafka/FastKafka/#fastkafka.FastKafka)\n",
    "app is named as `kafka_app`\n",
    "[`FastKafka`](https://airtai.github.io/fastkafka/0.5.0rc0/api/fastkafka/FastKafka/#fastkafka.FastKafka)\"\"\"\n",
    "\n",
    "expected = \"\"\"In the above example,\n",
    "[`FastKafka`](https://airtai.github.io/fastkafka/docs/api/fastkafka#fastkafka.FastKafka)\n",
    "[`FastKafka`](https://airtai.github.io/fastkafka/docs/api/fastkafka#fastkafka.FastKafka)\n",
    "app is named as `kafka_app`\n",
    "[`FastKafka`](https://airtai.github.io/fastkafka/docs/api/fastkafka#fastkafka.FastKafka)\"\"\"\n",
    "\n",
    "dir_prefix = \"../\"\n",
    "doc_host=\"https://airtai.github.io\"\n",
    "doc_baseurl=\"/fastkafka\"\n",
    "actual = _fix_symbol_links(fixture, dir_prefix, doc_host, doc_baseurl, use_relative_doc_links=False)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9bbfbe81",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "In the above example,\n",
      "[`FastKafka`](../api/fastkafka/FastKafka.md#fastkafka.FastKafka)\n",
      "[`FastKafka`](../api/fastkafka/FastKafka.md#fastkafka.FastKafka)\n",
      "app is named as `kafka_app`\n",
      "[`FastKafka`](../api/fastkafka/FastKafka.md#fastkafka.FastKafka)\n"
     ]
    }
   ],
   "source": [
    "fixture = \"\"\"In the above example,\n",
    "[`FastKafka`](https://airtai.github.io/fastkafka/api/fastkafka/FastKafka/#fastkafka.FastKafka)\n",
    "[`FastKafka`](https://airtai.github.io/fastkafka/0.5.0/api/fastkafka/FastKafka/#fastkafka.FastKafka)\n",
    "app is named as `kafka_app`\n",
    "[`FastKafka`](https://airtai.github.io/fastkafka/0.5.0rc0/api/fastkafka/FastKafka/#fastkafka.FastKafka)\"\"\"\n",
    "\n",
    "expected = \"\"\"In the above example,\n",
    "[`FastKafka`](../api/fastkafka/FastKafka.md#fastkafka.FastKafka)\n",
    "[`FastKafka`](../api/fastkafka/FastKafka.md#fastkafka.FastKafka)\n",
    "app is named as `kafka_app`\n",
    "[`FastKafka`](../api/fastkafka/FastKafka.md#fastkafka.FastKafka)\"\"\"\n",
    "\n",
    "dir_prefix = \"../\"\n",
    "doc_host=\"https://airtai.github.io\"\n",
    "doc_baseurl=\"/fastkafka\"\n",
    "actual = _fix_symbol_links(fixture, dir_prefix, doc_host, doc_baseurl)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "54241bdf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "In the above example,\n",
      "[`FastKafka`](./api/fastkafka/FastKafka.md#fastkafka.FastKafka)\n",
      "[`FastKafka`](./api/fastkafka/FastKafka.md#fastkafka.FastKafka)\n",
      "app is named as `kafka_app`\n",
      "[`FastKafka`](./api/fastkafka/FastKafka.md#fastkafka.FastKafka)\n"
     ]
    }
   ],
   "source": [
    "fixture = \"\"\"In the above example,\n",
    "[`FastKafka`](https://airtai.github.io/fastkafka/api/fastkafka/FastKafka/#fastkafka.FastKafka)\n",
    "[`FastKafka`](https://airtai.github.io/fastkafka/0.5.0/api/fastkafka/FastKafka/#fastkafka.FastKafka)\n",
    "app is named as `kafka_app`\n",
    "[`FastKafka`](https://airtai.github.io/fastkafka/0.5.0rc0/api/fastkafka/FastKafka/#fastkafka.FastKafka)\"\"\"\n",
    "\n",
    "expected = \"\"\"In the above example,\n",
    "[`FastKafka`](./api/fastkafka/FastKafka.md#fastkafka.FastKafka)\n",
    "[`FastKafka`](./api/fastkafka/FastKafka.md#fastkafka.FastKafka)\n",
    "app is named as `kafka_app`\n",
    "[`FastKafka`](./api/fastkafka/FastKafka.md#fastkafka.FastKafka)\"\"\"\n",
    "\n",
    "dir_prefix = \"\"\n",
    "doc_host=\"https://airtai.github.io\"\n",
    "doc_baseurl=\"/fastkafka\"\n",
    "actual = _fix_symbol_links(fixture, dir_prefix, doc_host, doc_baseurl)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3a51204c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The service can be tested using the\n",
      "[`Tester`](https://airtai.github.io/fastkafka/docs/api/fastkafka/testing/Tester#fastkafka.testing.Tester)\n",
      "[`Tester`](https://airtai.github.io/fastkafka/docs/api/fastkafka/testing/Tester#fastkafka.testing.Tester)\n",
      "[`Tester`](https://airtai.github.io/fastkafka/docs/api/fastkafka/testing/Tester#fastkafka.testing.Tester)\n",
      "instance and we can start the Kafka\n",
      "broker locally using the\n",
      "[`ApacheKafkaBroker`](https://airtai.github.io/fastkafka/docs/api/fastkafka/testing/ApacheKafkaBroker#fastkafka.testing.ApacheKafkaBroker).\n"
     ]
    }
   ],
   "source": [
    "fixture = \"\"\"The service can be tested using the\n",
    "[`Tester`](https://airtai.github.io/fastkafka/api/fastkafka/testing/Tester/#fastkafka.testing.Tester)\n",
    "[`Tester`](https://airtai.github.io/fastkafka/0.5.0/api/fastkafka/testing/Tester/#fastkafka.testing.Tester)\n",
    "[`Tester`](https://airtai.github.io/fastkafka/dev/api/fastkafka/testing/Tester/#fastkafka.testing.Tester)\n",
    "instance and we can start the Kafka\n",
    "broker locally using the\n",
    "[`ApacheKafkaBroker`](https://airtai.github.io/fastkafka/api/fastkafka/testing/ApacheKafkaBroker/#fastkafka.testing.ApacheKafkaBroker).\"\"\"\n",
    "\n",
    "expected = {\n",
    "    \"relative_url_False\": \"\"\"The service can be tested using the\n",
    "[`Tester`](https://airtai.github.io/fastkafka/docs/api/fastkafka/testing/Tester#fastkafka.testing.Tester)\n",
    "[`Tester`](https://airtai.github.io/fastkafka/docs/api/fastkafka/testing/Tester#fastkafka.testing.Tester)\n",
    "[`Tester`](https://airtai.github.io/fastkafka/docs/api/fastkafka/testing/Tester#fastkafka.testing.Tester)\n",
    "instance and we can start the Kafka\n",
    "broker locally using the\n",
    "[`ApacheKafkaBroker`](https://airtai.github.io/fastkafka/docs/api/fastkafka/testing/ApacheKafkaBroker#fastkafka.testing.ApacheKafkaBroker).\"\"\",\n",
    "    \"relative_url_True\": \"\"\"The service can be tested using the\n",
    "[`Tester`](./api/fastkafka/testing/Tester.md#fastkafka.testing.Tester)\n",
    "[`Tester`](./api/fastkafka/testing/Tester.md#fastkafka.testing.Tester)\n",
    "[`Tester`](./api/fastkafka/testing/Tester.md#fastkafka.testing.Tester)\n",
    "instance and we can start the Kafka\n",
    "broker locally using the\n",
    "[`ApacheKafkaBroker`](./api/fastkafka/testing/ApacheKafkaBroker.md#fastkafka.testing.ApacheKafkaBroker).\"\"\",\n",
    "}\n",
    "\n",
    "dir_prefix = \"\"\n",
    "doc_host = \"https://airtai.github.io\"\n",
    "doc_baseurl = \"/fastkafka\"\n",
    "\n",
    "for flag in [False]:\n",
    "    actual = _fix_symbol_links(fixture, dir_prefix, doc_host, doc_baseurl, flag)\n",
    "    print(actual)\n",
    "    assert actual == expected[f\"relative_url_{flag}\"], expected[f\"relative_url_{flag}\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "78e866b0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "This is not a link to a symbol: https://www.google.com\n"
     ]
    }
   ],
   "source": [
    "fixture = \"\"\"This is not a link to a symbol: https://www.google.com\"\"\"\n",
    "\n",
    "expected = \"\"\"This is not a link to a symbol: https://www.google.com\"\"\"\n",
    "\n",
    "dir_prefix = \"\"\n",
    "doc_host=\"https://airtai.github.io\"\n",
    "doc_baseurl=\"/fastkafka\"\n",
    "actual = _fix_symbol_links(fixture, dir_prefix, doc_host, doc_baseurl)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d33bf47d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A sample fastkafka-based library that uses Redpanda for testing, based\n",
      "on this guide, can be found\n",
      "[here](https://github.com/airtai/sample_fastkafka_with_redpanda)\n"
     ]
    }
   ],
   "source": [
    "fixture = \"\"\"A sample fastkafka-based library that uses Redpanda for testing, based\n",
    "on this guide, can be found\n",
    "[here](https://github.com/airtai/sample_fastkafka_with_redpanda)\"\"\"\n",
    "\n",
    "expected = \"\"\"A sample fastkafka-based library that uses Redpanda for testing, based\n",
    "on this guide, can be found\n",
    "[here](https://github.com/airtai/sample_fastkafka_with_redpanda)\"\"\"\n",
    "\n",
    "dir_prefix = \"\"\n",
    "doc_host=\"https://airtai.github.io\"\n",
    "doc_baseurl=\"/fastkafka\"\n",
    "actual = _fix_symbol_links(fixture, dir_prefix, doc_host, doc_baseurl)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "617b2f5d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "To learn more about Redpanda, please visit their\n",
      "[website](https://redpanda.com/) or checkout this [blog\n",
      "post](https://redpanda.com/blog/redpanda-vs-kafka-performance-benchmark)\n",
      "comparing Redpanda and Kafka’s performance benchmarks.\n"
     ]
    }
   ],
   "source": [
    "fixture = \"\"\"To learn more about Redpanda, please visit their\n",
    "[website](https://redpanda.com/) or checkout this [blog\n",
    "post](https://redpanda.com/blog/redpanda-vs-kafka-performance-benchmark)\n",
    "comparing Redpanda and Kafka’s performance benchmarks.\"\"\"\n",
    "\n",
    "expected = \"\"\"To learn more about Redpanda, please visit their\n",
    "[website](https://redpanda.com/) or checkout this [blog\n",
    "post](https://redpanda.com/blog/redpanda-vs-kafka-performance-benchmark)\n",
    "comparing Redpanda and Kafka’s performance benchmarks.\"\"\"\n",
    "\n",
    "dir_prefix = \"\"\n",
    "doc_host=\"https://airtai.github.io\"\n",
    "doc_baseurl=\"/fastkafka\"\n",
    "actual = _fix_symbol_links(fixture, dir_prefix, doc_host, doc_baseurl)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "06c76c04",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "def _get_relative_url_prefix(docs_path: Path, sub_path: Path) -> str:\n",
    "    \"\"\"Returns a relative url prefix from a sub path to a docs path.\n",
    "\n",
    "    Args:\n",
    "        docs_path (Path): The docs directory path.\n",
    "        sub_path (Path): The sub directory path.\n",
    "\n",
    "    Returns:\n",
    "        str: A string representing the relative path from the sub path to the docs path.\n",
    "\n",
    "    Raises:\n",
    "        ValueError: If the sub path is not a descendant of the docs path.\n",
    "    \"\"\"\n",
    "    try:\n",
    "        relative_path = sub_path.relative_to(docs_path)\n",
    "    except ValueError:\n",
    "        raise ValueError(f\"{sub_path} is not a descendant of {docs_path}\")\n",
    "\n",
    "    return (\n",
    "        \"../\" * (len(relative_path.parts) - 1) if len(relative_path.parts) > 1 else \"\"\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0d0a4524",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "../\n",
      "../../\n"
     ]
    }
   ],
   "source": [
    "docs_path = Path('docusaurus/docs')\n",
    "\n",
    "sub_path = Path('docusaurus/docs/index.md')\n",
    "actual = _get_relative_url_prefix(docs_path, sub_path) \n",
    "print(actual)\n",
    "assert actual == \"\"\n",
    "\n",
    "sub_path = Path('docusaurus/docs/guides/Guide_31_Using_redpanda_to_test_fastkafka.md')\n",
    "actual = _get_relative_url_prefix(docs_path, sub_path)\n",
    "print(actual)\n",
    "assert actual == \"../\"\n",
    "\n",
    "sub_path = Path('docusaurus/docs/guides/tutorial/fastkafka.md')\n",
    "actual = _get_relative_url_prefix(docs_path, sub_path)\n",
    "print(actual)\n",
    "assert actual == \"../../\"\n",
    "\n",
    "with pytest.raises(ValueError) as e:\n",
    "    sub_path = Path('mkdocs/docs/guides/tutorial/fastkafka.md')\n",
    "    _get_relative_url_prefix(docs_path, sub_path)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "769c8693",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def fix_invalid_syntax_in_markdown(docs_path: str) -> None:\n",
    "    \"\"\"Fix invalid HTML syntax in markdown files and converts inline style attributes to JSX-compatible format.\n",
    "\n",
    "    Args:\n",
    "        docs_path: The path to the root directory to search for markdown files.\n",
    "    \"\"\"\n",
    "    cfg = get_config()\n",
    "    doc_host = cfg[\"doc_host\"]\n",
    "    doc_baseurl = cfg[\"doc_baseurl\"]\n",
    "\n",
    "    markdown_files = _get_all_markdown_files_path(Path(docs_path))\n",
    "    for file in markdown_files:\n",
    "        relative_url_prefix = _get_relative_url_prefix(Path(docs_path), file)\n",
    "        contents = Path(file).read_text()\n",
    "\n",
    "        contents = _convert_html_style_attribute_to_jsx(contents)\n",
    "        contents = _fix_special_symbols_in_html(contents)\n",
    "        contents = _fix_symbol_links(\n",
    "            contents, relative_url_prefix, doc_host, doc_baseurl\n",
    "        )\n",
    "        file.write_text(contents)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ccf9e23b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "************************************************************************************************************************\n",
      "source some text goes here Test and one more tag Test\n",
      "[`FastKafka`](./api/fastkafka/FastKafka.md#fastkafka.FastKafka)\n",
      "[`Tester`](./api/fastkafka/testing/Tester.md#fastkafka.testing.Tester)\n",
      "[here](https://github.com/airtai/sample_fastkafka_with_redpanda)\n",
      "\n",
      "************************************************************************************************************************\n",
      "source some text goes here Test and one more tag Test\n",
      "[`FastKafka`](../api/fastkafka/FastKafka.md#fastkafka.FastKafka)\n",
      "[`Tester`](../api/fastkafka/testing/Tester.md#fastkafka.testing.Tester)\n",
      "[here](https://github.com/airtai/sample_fastkafka_with_redpanda)\n",
      "\n",
      "************************************************************************************************************************\n",
      "source some text goes here Test and one more tag Test\n",
      "[`FastKafka`](../api/fastkafka/FastKafka.md#fastkafka.FastKafka)\n",
      "[`Tester`](../api/fastkafka/testing/Tester.md#fastkafka.testing.Tester)\n",
      "[here](https://github.com/airtai/sample_fastkafka_with_redpanda)\n",
      "\n",
      "************************************************************************************************************************\n",
      "source some text goes here Test and one more tag Test\n",
      "[`FastKafka`](../../api/fastkafka/FastKafka.md#fastkafka.FastKafka)\n",
      "[`Tester`](../../api/fastkafka/testing/Tester.md#fastkafka.testing.Tester)\n",
      "[here](https://github.com/airtai/sample_fastkafka_with_redpanda)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "with TemporaryDirectory() as d:\n",
    "    module_name = \"fastkafka\"\n",
    "\n",
    "    docs_path = Path(d) / \"docusaurus\" / \"docs\"\n",
    "    docs_path.mkdir(parents=True)\n",
    "\n",
    "    api_path = docs_path / \"api\"\n",
    "    api_path.mkdir(parents=True)\n",
    "\n",
    "    blog_path = docs_path / \"blog\"\n",
    "    blog_path.mkdir(parents=True)\n",
    "\n",
    "    nested_api_path = api_path / \"fastKafka\"\n",
    "    nested_api_path.mkdir(parents=True)\n",
    "\n",
    "    for p in [docs_path, api_path, blog_path, nested_api_path]:\n",
    "        with open((p / \"file.md\"), \"w\") as f:\n",
    "            f.write(\n",
    "                \"\"\"source some text goes here Test and one more tag Test\n",
    "[`FastKafka`](https://airtai.github.io/fastkafka/api/fastkafka/FastKafka/#fastkafka.FastKafka)\n",
    "[`Tester`](https://airtai.github.io/fastkafka/api/fastkafka/testing/Tester/#fastkafka.testing.Tester)\n",
    "[here](https://github.com/airtai/sample_fastkafka_with_redpanda)\n",
    "\"\"\"\n",
    "            )\n",
    "\n",
    "    fix_invalid_syntax_in_markdown(str(docs_path))\n",
    "    expected = [\n",
    "        \"\"\"source some text goes here Test and one more tag Test\n",
    "[`FastKafka`](./api/fastkafka/FastKafka.md#fastkafka.FastKafka)\n",
    "[`Tester`](./api/fastkafka/testing/Tester.md#fastkafka.testing.Tester)\n",
    "[here](https://github.com/airtai/sample_fastkafka_with_redpanda)\n",
    "\"\"\",\n",
    "        \"\"\"source some text goes here Test and one more tag Test\n",
    "[`FastKafka`](../api/fastkafka/FastKafka.md#fastkafka.FastKafka)\n",
    "[`Tester`](../api/fastkafka/testing/Tester.md#fastkafka.testing.Tester)\n",
    "[here](https://github.com/airtai/sample_fastkafka_with_redpanda)\n",
    "\"\"\",\n",
    "        \"\"\"source some text goes here Test and one more tag Test\n",
    "[`FastKafka`](../api/fastkafka/FastKafka.md#fastkafka.FastKafka)\n",
    "[`Tester`](../api/fastkafka/testing/Tester.md#fastkafka.testing.Tester)\n",
    "[here](https://github.com/airtai/sample_fastkafka_with_redpanda)\n",
    "\"\"\",\n",
    "        \"\"\"source some text goes here Test and one more tag Test\n",
    "[`FastKafka`](../../api/fastkafka/FastKafka.md#fastkafka.FastKafka)\n",
    "[`Tester`](../../api/fastkafka/testing/Tester.md#fastkafka.testing.Tester)\n",
    "[here](https://github.com/airtai/sample_fastkafka_with_redpanda)\n",
    "\"\"\",\n",
    "    ]\n",
    "\n",
    "    for i, p in enumerate([docs_path, api_path, blog_path, nested_api_path]):\n",
    "        with open((p / \"file.md\"), \"r\") as f:\n",
    "            actual = f.read()\n",
    "            print(\"*\" * 120)\n",
    "            print(actual)\n",
    "            assert actual == expected[i]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ef4c7fa5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def generate_markdown_docs(module_name: str, docs_path: str) -> None:\n",
    "    \"\"\"Generates Markdown documentation files for the symbols in the given module and save them to the given directory.\n",
    "\n",
    "    Args:\n",
    "        module_name: The name of the module to generate documentation for.\n",
    "        docs_path: The path to the directory where the documentation files will be saved.\n",
    "    \"\"\"\n",
    "    members_with_submodules = _get_submodules(module_name)\n",
    "    symbols = _load_submodules(module_name, members_with_submodules)\n",
    "    lib_version = get_config()[\"version\"]\n",
    "    \n",
    "    for symbol in symbols:\n",
    "        content = _get_formatted_docstring_for_symbol(symbol, lib_version)\n",
    "        target_file_path = (\n",
    "            \"/\".join(f\"{symbol.__module__}.{symbol.__name__}\".split(\".\")) + \".md\"\n",
    "        )\n",
    "        with open((Path(docs_path) / \"api\" / target_file_path), \"w\") as f:\n",
    "            f.write(content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d95dbe68",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.1.0\n"
     ]
    }
   ],
   "source": [
    "@contextmanager\n",
    "def mock_get_config(lib_version):\n",
    "    with patch('__main__.get_config') as mock_get_config:\n",
    "        mock_get_config.return_value = {\"version\": lib_version}\n",
    "        yield\n",
    "        \n",
    "with mock_get_config(lib_version=\"0.1.0\"):\n",
    "    actual = get_config()[\"version\"]\n",
    "    print(actual)\n",
    "    expected = \"0.1.0\"\n",
    "    assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "39985ae4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "## mypackage_904.mymodule.FixtureClass {#mypackage_904.mymodule.FixtureClass}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "\n",
      "Fixture documentation\n",
      "\n",
      "### __init__ {#mypackage_904.mymodule.FixtureClass.init}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "__init__(\n",
      "    self, attribute\n",
      ")\n",
      "```\n",
      "\n",
      "__init__ documentation url=HttpUrl(\" https://www.google.co.uk \", )\n",
      "\n",
      "### abstract_method {#mypackage_904.mymodule.FixtureClass.abstract_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@abstractmethod\n",
      "abstract_method(\n",
      "    self\n",
      ")\n",
      "```\n",
      "\n",
      "abstract_method documentation\n",
      "\n",
      "### class_method {#mypackage_904.mymodule.FixtureClass.class_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@classmethod\n",
      "class_method()\n",
      "```\n",
      "\n",
      "class_method documentation\n",
      "\n",
      "### instance_method {#mypackage_904.mymodule.FixtureClass.instance_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "instance_method(\n",
      "    self\n",
      ")\n",
      "```\n",
      "\n",
      "instance_method documentation\n",
      "\n",
      "### patched_method_in_same_file {#mypackage_904.mymodule.FixtureClass.patched_method_in_same_file}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "patched_method_in_same_file(\n",
      "    self, s\n",
      ")\n",
      "```\n",
      "\n",
      "I am a patched method in the same file\n",
      "\n",
      "### property_attribute {#mypackage_904.mymodule.FixtureClass.property_attribute}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@property\n",
      "property_attribute(\n",
      "    self\n",
      ")\n",
      "```\n",
      "\n",
      "property_attribute documentation\n",
      "\n",
      "### static_method {#mypackage_904.mymodule.FixtureClass.static_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/0.1.0/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@staticmethod\n",
      "static_method()\n",
      "```\n",
      "\n",
      "static_method documentation\n",
      "\n",
      "\n",
      "## mypackage_416.mymodule.FixtureClass {#mypackage_416.mymodule.FixtureClass}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "\n",
      "Fixture documentation\n",
      "\n",
      "### __init__ {#mypackage_416.mymodule.FixtureClass.init}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "__init__(\n",
      "    self, attribute\n",
      ")\n",
      "```\n",
      "\n",
      "__init__ documentation url=HttpUrl(\" https://www.google.co.uk \", )\n",
      "\n",
      "### abstract_method {#mypackage_416.mymodule.FixtureClass.abstract_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@abstractmethod\n",
      "abstract_method(\n",
      "    self\n",
      ")\n",
      "```\n",
      "\n",
      "abstract_method documentation\n",
      "\n",
      "### class_method {#mypackage_416.mymodule.FixtureClass.class_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@classmethod\n",
      "class_method()\n",
      "```\n",
      "\n",
      "class_method documentation\n",
      "\n",
      "### instance_method {#mypackage_416.mymodule.FixtureClass.instance_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "instance_method(\n",
      "    self\n",
      ")\n",
      "```\n",
      "\n",
      "instance_method documentation\n",
      "\n",
      "### patched_method_in_same_file {#mypackage_416.mymodule.FixtureClass.patched_method_in_same_file}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "patched_method_in_same_file(\n",
      "    self, s\n",
      ")\n",
      "```\n",
      "\n",
      "I am a patched method in the same file\n",
      "\n",
      "### property_attribute {#mypackage_416.mymodule.FixtureClass.property_attribute}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@property\n",
      "property_attribute(\n",
      "    self\n",
      ")\n",
      "```\n",
      "\n",
      "property_attribute documentation\n",
      "\n",
      "### static_method {#mypackage_416.mymodule.FixtureClass.static_method}\n",
      "\n",
      "<a href=\"https://github.com/airtai/fastkafka/blob/main/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\n",
      "\n",
      "```py\n",
      "@staticmethod\n",
      "static_method()\n",
      "```\n",
      "\n",
      "static_method documentation\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "module_code = '''\n",
    "\n",
    "__all__ = ['FixtureClass']\n",
    "\n",
    "from typing import *\n",
    "from abc import abstractmethod\n",
    "\n",
    "from fastcore.basics import patch\n",
    "\n",
    "class FixtureClass:\n",
    "    \"\"\"Fixture documentation\"\"\"\n",
    "    \n",
    "    def __init__(self, attribute):\n",
    "        \"\"\"__init__ documentation url=HttpUrl(\"https://www.google.co.uk\", )\"\"\"\n",
    "        self.attribute = attribute\n",
    "        \n",
    "    def __str__(self):\n",
    "        \"\"\"__str__ documentation\"\"\"\n",
    "        return f\"MyClass instance with attribute: {self.attribute}\"\n",
    "    \n",
    "    @property\n",
    "    def property_attribute(self):\n",
    "        \"\"\"property_attribute documentation\"\"\"\n",
    "        return self.attribute\n",
    "    \n",
    "    @classmethod\n",
    "    def class_method(cls):\n",
    "        \"\"\"class_method documentation\"\"\"\n",
    "        return cls.class_variable\n",
    "    \n",
    "    @staticmethod\n",
    "    def static_method():\n",
    "        \"\"\"static_method documentation\"\"\"\n",
    "        return \"This is a static method\"\n",
    "    \n",
    "    def instance_method(self):\n",
    "        \"\"\"instance_method documentation\"\"\"\n",
    "        return \"This is an instance method\"\n",
    "    \n",
    "    @abstractmethod\n",
    "    def abstract_method(self):\n",
    "        \"\"\"abstract_method documentation\"\"\"\n",
    "        pass\n",
    "        \n",
    "@patch\n",
    "def patched_method_in_same_file(self:FixtureClass, s: str) -> None: \n",
    "    \"\"\"I am a patched method in the same file\"\"\"\n",
    "    pass\n",
    "'''\n",
    "\n",
    "for lib_version in [\"0.1.0\", \"0.1.1rc0\"]:\n",
    "    with TemporaryDirectory() as d:\n",
    "        my_package = f\"mypackage_{random.randint(0, 1000)}\"\n",
    "        module_name = \"mymodule\"\n",
    "\n",
    "        docs_path = Path(d) / \"docusaurus\" / \"docs\"\n",
    "        docs_path.mkdir(parents=True)\n",
    "\n",
    "        api_path = docs_path / \"api\" / my_package / module_name\n",
    "        api_path.mkdir(parents=True)\n",
    "\n",
    "        my_package_path = Path(d) / my_package\n",
    "        my_package_path.mkdir(parents=True)\n",
    "\n",
    "        file_path = my_package_path / f\"{module_name}.py\"\n",
    "\n",
    "        with open(file_path, \"w\", encoding=\"utf-8\") as file:\n",
    "            file.write(module_code)\n",
    "\n",
    "        with open((my_package_path / \"__init__.py\"), \"w\") as f:\n",
    "            f.write(f'__version__ = \"{lib_version}\"')\n",
    "\n",
    "        with add_tmp_path_to_sys_path(d):\n",
    "            with mock_get_config(lib_version=lib_version):\n",
    "                with mock_custom_nbdev_lookup():\n",
    "                    members_with_submodules = _get_submodules(my_package)\n",
    "                    symbols = _load_submodules(my_package, members_with_submodules)\n",
    "                    generate_markdown_docs(my_package, str(docs_path))\n",
    "\n",
    "        with open(api_path / \"FixtureClass.md\", \"r\", encoding=\"utf-8\") as file:\n",
    "            actual = file.read()\n",
    "    \n",
    "    gh_tag = lib_version if lib_version.replace(\".\", \"\").isdigit() else \"main\"\n",
    "    expected = f\"## {my_package}\" + \".mymodule.FixtureClass {#\" + my_package + '.mymodule.FixtureClass}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/' + gh_tag + '/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n\\nFixture documentation\\n\\n### __init__ {#' + my_package + '.mymodule.FixtureClass.init}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/' + gh_tag + '/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n__init__(\\n    self, attribute\\n)\\n```\\n\\n__init__ documentation url=HttpUrl(\" https://www.google.co.uk \", )\\n\\n### abstract_method {#' + my_package + '.mymodule.FixtureClass.abstract_method}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/' + gh_tag + '/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n@abstractmethod\\nabstract_method(\\n    self\\n)\\n```\\n\\nabstract_method documentation\\n\\n### class_method {#' + my_package + '.mymodule.FixtureClass.class_method}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/' + gh_tag + '/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n@classmethod\\nclass_method()\\n```\\n\\nclass_method documentation\\n\\n### instance_method {#' + my_package + '.mymodule.FixtureClass.instance_method}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/' + gh_tag + '/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\ninstance_method(\\n    self\\n)\\n```\\n\\ninstance_method documentation\\n\\n### patched_method_in_same_file {#' + my_package + '.mymodule.FixtureClass.patched_method_in_same_file}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/' + gh_tag + '/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\npatched_method_in_same_file(\\n    self, s\\n)\\n```\\n\\nI am a patched method in the same file\\n\\n### property_attribute {#' + my_package + '.mymodule.FixtureClass.property_attribute}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/' + gh_tag + '/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n@property\\nproperty_attribute(\\n    self\\n)\\n```\\n\\nproperty_attribute documentation\\n\\n### static_method {#' + my_package + '.mymodule.FixtureClass.static_method}\\n\\n<a href=\"https://github.com/airtai/fastkafka/blob/' + gh_tag + '/fastkafka/_application/app.py#L171-L425\" class=\"link-to-source\" target=\"_blank\">View source</a>\\n\\n```py\\n@staticmethod\\nstatic_method()\\n```\\n\\nstatic_method documentation\\n\\n'\n",
    "    print(actual)\n",
    "    assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a7b556ef",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "def _parse_lines(lines: List[str]) -> Tuple[List[str], int]:\n",
    "    \"\"\"Parse a list of lines and return a tuple containing a list of filenames and an index indicating how many lines to skip.\n",
    "\n",
    "    Args:\n",
    "        lines: A list of strings representing lines of input text.\n",
    "\n",
    "    Returns:\n",
    "        A tuple containing a list of strings representing the filenames extracted\n",
    "        from links in the lines and an integer representing the number of lines to skip.\n",
    "    \"\"\"\n",
    "    index = next(\n",
    "        (i for i, line in enumerate(lines) if not line.strip().startswith(\"- [\")),\n",
    "        len(lines),\n",
    "    )\n",
    "    return [line.split(\"(\")[1][:-4] for line in lines[:index]], index"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bc6fdfe9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(['api/fastkafka/encoder/json_encoder'], 1)\n"
     ]
    }
   ],
   "source": [
    "fixture = ['            - [json_encoder](api/fastkafka/encoder/json_encoder.md)', '        - testing', '            - [ApacheKafkaBroker](api/fastkafka/testing/ApacheKafkaBroker.md)', '            - [LocalRedpandaBroker](api/fastkafka/testing/LocalRedpandaBroker.md)', '            - [Tester](api/fastkafka/testing/Tester.md)']\n",
    "expected = (['api/fastkafka/encoder/json_encoder'], 1)\n",
    "\n",
    "actual = _parse_lines(fixture)\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected, actual"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b816ec5c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(['api/fastkafka/testing/ApacheKafkaBroker', 'api/fastkafka/testing/LocalRedpandaBroker', 'api/fastkafka/testing/Tester'], 3)\n"
     ]
    }
   ],
   "source": [
    "\n",
    "fixture = ['            - [ApacheKafkaBroker](api/fastkafka/testing/ApacheKafkaBroker.md)', '            - [LocalRedpandaBroker](api/fastkafka/testing/LocalRedpandaBroker.md)', '            - [Tester](api/fastkafka/testing/Tester.md)']\n",
    "expected = (['api/fastkafka/testing/ApacheKafkaBroker', 'api/fastkafka/testing/LocalRedpandaBroker', 'api/fastkafka/testing/Tester'], 3)\n",
    "\n",
    "actual = _parse_lines(fixture)\n",
    "print(actual)\n",
    "\n",
    "assert actual == expected, actual"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d5be29aa",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "def _parse_section(text: str, ignore_first_line: bool = False) -> List[Any]:\n",
    "    \"\"\"Parse the given section contents and return a list of file names in the expected format.\n",
    "\n",
    "    Args:\n",
    "        text: A string representing the contents of a file.\n",
    "        ignore_first_line: Flag indicating whether to ignore the first line extracting the section contents.\n",
    "\n",
    "    Returns:\n",
    "        A list of filenames in the expected format\n",
    "    \"\"\"\n",
    "    pattern = r\"\\[.*?\\]\\((.*?)\\)|\\[(.*?)\\]\\[(.*?)\\]\"\n",
    "    lines = text.split(\"\\n\")[1:] if ignore_first_line else text.split(\"\\n\")\n",
    "    ret_val = []\n",
    "    index = 0\n",
    "    while index < len(lines):\n",
    "        line = lines[index]\n",
    "        match = re.search(pattern, line.strip())\n",
    "        if match is not None:\n",
    "            ret_val.append(match.group(1).split(\".md\")[0])\n",
    "            index += 1\n",
    "        elif line.strip() != \"\":\n",
    "            value, skip_lines = _parse_lines(lines[index + 1 :])\n",
    "            ret_val.append({line.replace(\"-\", \"\").strip(): value})\n",
    "            index += skip_lines + 1\n",
    "        else:\n",
    "            index += 1\n",
    "    return ret_val"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "87a06535",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['api/fastkafka/FastKafka',\n",
       " 'api/fastkafka/KafkaEvent',\n",
       " {'encoder': ['api/fastkafka/encoder/json_encoder']},\n",
       " {'testing': ['api/fastkafka/testing/ApacheKafkaBroker',\n",
       "   'api/fastkafka/testing/LocalRedpandaBroker',\n",
       "   'api/fastkafka/testing/Tester']}]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fixture = \"\"\"    - fastkafka\n",
    "        - [FastKafka](api/fastkafka/FastKafka.md)\n",
    "        - [KafkaEvent](api/fastkafka/KafkaEvent.md)\n",
    "        - encoder\n",
    "            - [json_encoder](api/fastkafka/encoder/json_encoder.md)\n",
    "        - testing\n",
    "            - [ApacheKafkaBroker](api/fastkafka/testing/ApacheKafkaBroker.md)\n",
    "            - [LocalRedpandaBroker](api/fastkafka/testing/LocalRedpandaBroker.md)\n",
    "            - [Tester](api/fastkafka/testing/Tester.md)\n",
    "\"\"\"\n",
    "\n",
    "expected = [\n",
    "    \"api/fastkafka/FastKafka\",\n",
    "    \"api/fastkafka/KafkaEvent\",\n",
    "    {\"encoder\": [\"api/fastkafka/encoder/json_encoder\"]},\n",
    "    {\n",
    "        \"testing\": [\n",
    "            \"api/fastkafka/testing/ApacheKafkaBroker\",\n",
    "            \"api/fastkafka/testing/LocalRedpandaBroker\",\n",
    "            \"api/fastkafka/testing/Tester\",\n",
    "        ]\n",
    "    }\n",
    "]\n",
    "\n",
    "ignore_first_line = True\n",
    "actual = _parse_section(fixture, ignore_first_line)\n",
    "display(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "399d66b1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'Writing services': ['guides/Guide_11_Consumes_Basics',\n",
       "   'guides/Guide_21_Produces_Basics']},\n",
       " {'Testing': ['guides/Guide_31_Using_redpanda_to_test_fastkafka']}]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fixture = \"\"\"    - Writing services\n",
    "        - [@consumes basics](guides/Guide_11_Consumes_Basics.md)\n",
    "        - [@consumes basics](guides/Guide_21_Produces_Basics.md)\n",
    "    - Testing\n",
    "        - [Using Redpanda to test FastKafka](guides/Guide_31_Using_redpanda_to_test_fastkafka.md)\n",
    "\"\"\"\n",
    "\n",
    "expected = [\n",
    "    {\n",
    "        \"Writing services\": [\n",
    "            \"guides/Guide_11_Consumes_Basics\",\n",
    "            \"guides/Guide_21_Produces_Basics\",\n",
    "        ],\n",
    "    },\n",
    "    {\n",
    "        \"Testing\": [\"guides/Guide_31_Using_redpanda_to_test_fastkafka\"],\n",
    "    },\n",
    "]\n",
    "\n",
    "actual = _parse_section(fixture)\n",
    "display(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9212e84e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "def _get_section_from_markdown(\n",
    "    markdown_text: str, section_header: str\n",
    ") -> Optional[str]:\n",
    "    \"\"\"Get the contents of the section header from the given markdown text\n",
    "\n",
    "    Args:\n",
    "        markdown_text: A string containing the markdown text to extract the section from.\n",
    "        section_header: A string representing the header of the section to extract.\n",
    "\n",
    "    Returns:\n",
    "        A string representing the contents of the section header if the section header\n",
    "        is present in the markdown text, else None\n",
    "    \"\"\"\n",
    "    pattern = re.compile(rf\"^- {section_header}\\n((?:\\s+- .*\\n)+)\", re.M)\n",
    "    match = pattern.search(markdown_text)\n",
    "    return match.group(1) if match else None"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ea297651",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "    - fastkafka\n",
      "        - [FastKafka](api/fastkafka/FastKafka.md)\n",
      "        - [KafkaEvent](api/fastkafka/KafkaEvent.md)\n",
      "        - encoder\n",
      "            - [json_encoder](api/fastkafka/encoder/json_encoder.md)\n",
      "        - testing\n",
      "            - [ApacheKafkaBroker](api/fastkafka/testing/ApacheKafkaBroker.md)\n",
      "            - [LocalRedpandaBroker](api/fastkafka/testing/LocalRedpandaBroker.md)\n",
      "            - [Tester](api/fastkafka/testing/Tester.md)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "summary = \"\"\"- [FastKafka](index.md)\n",
    "- Guides\n",
    "    - Writing services\n",
    "        - [@consumes basics](guides/Guide_11_Consumes_Basics.md)\n",
    "        - [@consumes basics](guides/Guide_11_Consumes_Basics.md)\n",
    "    - Testing\n",
    "        - [Using Redpanda to test FastKafka](guides/Guide_31_Using_redpanda_to_test_fastkafka.md)\n",
    "- API\n",
    "    - fastkafka\n",
    "        - [FastKafka](api/fastkafka/FastKafka.md)\n",
    "        - [KafkaEvent](api/fastkafka/KafkaEvent.md)\n",
    "        - encoder\n",
    "            - [json_encoder](api/fastkafka/encoder/json_encoder.md)\n",
    "        - testing\n",
    "            - [ApacheKafkaBroker](api/fastkafka/testing/ApacheKafkaBroker.md)\n",
    "            - [LocalRedpandaBroker](api/fastkafka/testing/LocalRedpandaBroker.md)\n",
    "            - [Tester](api/fastkafka/testing/Tester.md)\n",
    "- CLI\n",
    "    - [fastkafka](cli/fastkafka.md)\n",
    "    - [run_fastkafka_server_process](cli/run_fastkafka_server_process.md)\n",
    "- [Releases](CHANGELOG.md)\"\"\"\n",
    "\n",
    "section_header = \"API\"\n",
    "expected = \"\"\"    - fastkafka\n",
    "        - [FastKafka](api/fastkafka/FastKafka.md)\n",
    "        - [KafkaEvent](api/fastkafka/KafkaEvent.md)\n",
    "        - encoder\n",
    "            - [json_encoder](api/fastkafka/encoder/json_encoder.md)\n",
    "        - testing\n",
    "            - [ApacheKafkaBroker](api/fastkafka/testing/ApacheKafkaBroker.md)\n",
    "            - [LocalRedpandaBroker](api/fastkafka/testing/LocalRedpandaBroker.md)\n",
    "            - [Tester](api/fastkafka/testing/Tester.md)\n",
    "\"\"\"\n",
    "actual = _get_section_from_markdown(summary, section_header)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fa11acce",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "    - [fastkafka](cli/fastkafka.md)\n",
      "    - [run_fastkafka_server_process](cli/run_fastkafka_server_process.md)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "section_header = \"CLI\"\n",
    "expected = \"\"\"    - [fastkafka](cli/fastkafka.md)\n",
    "    - [run_fastkafka_server_process](cli/run_fastkafka_server_process.md)\n",
    "\"\"\"\n",
    "actual = _get_section_from_markdown(summary, section_header)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2b41c549",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "    - Writing services\n",
      "        - [@consumes basics](guides/Guide_11_Consumes_Basics.md)\n",
      "        - [@consumes basics](guides/Guide_11_Consumes_Basics.md)\n",
      "    - Testing\n",
      "        - [Using Redpanda to test FastKafka](guides/Guide_31_Using_redpanda_to_test_fastkafka.md)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "section_header = \"Guides\"\n",
    "expected = \"\"\"    - Writing services\n",
    "        - [@consumes basics](guides/Guide_11_Consumes_Basics.md)\n",
    "        - [@consumes basics](guides/Guide_11_Consumes_Basics.md)\n",
    "    - Testing\n",
    "        - [Using Redpanda to test FastKafka](guides/Guide_31_Using_redpanda_to_test_fastkafka.md)\n",
    "\"\"\"\n",
    "actual = _get_section_from_markdown(summary, section_header)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "43f07f1a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "None\n"
     ]
    }
   ],
   "source": [
    "section_header = \"Invalid Section\"\n",
    "expected = None\n",
    "actual = _get_section_from_markdown(summary, section_header)\n",
    "print(actual)\n",
    "assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0c44fee7",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def generate_sidebar(\n",
    "    summary_file: str = \"./docusaurus/docs/SUMMARY.md\",\n",
    "    summary: str = \"\",\n",
    "    target: str = \"./docusaurus/sidebars.js\",\n",
    ") -> None:\n",
    "    \"\"\"\n",
    "    Generate a sidebar js file for a Docusaurus documentation site based on a SUMMARY.md file.\n",
    "\n",
    "    Args:\n",
    "        summary_file: The path to the SUMMARY.md file containing the documentation structure.\n",
    "            Default is \"./docusaurus/docs/SUMMARY.md\".\n",
    "        summary: An optional summary string.\n",
    "            Default is an empty string.\n",
    "        target: The path to the target sidebar js file to be generated.\n",
    "            Default is \"./docusaurus/sidebars.js\".\n",
    "\n",
    "    Returns:\n",
    "        None: The function does not return any value directly, but it generates a sidebar file.\n",
    "\n",
    "    Raises:\n",
    "        FileNotFoundError: If the specified `summary_file` does not exist.\n",
    "    \"\"\"\n",
    "    with open(summary_file, \"r\") as stream, open(target, \"w\") as target_stream:\n",
    "        summary_contents = stream.read()\n",
    "\n",
    "        guides_summary = _get_section_from_markdown(summary_contents, \"Guides\")\n",
    "        parsed_guides = _parse_section(guides_summary)  # type: ignore\n",
    "\n",
    "        api_summary = _get_section_from_markdown(summary_contents, \"API\")\n",
    "        parsed_api = _parse_section(api_summary, True)  # type: ignore\n",
    "\n",
    "        cli_summary = _get_section_from_markdown(summary_contents, \"CLI\")\n",
    "        parsed_cli = _parse_section(cli_summary)  # type: ignore\n",
    "\n",
    "        target_stream.write(\n",
    "            \"\"\"module.exports = {\n",
    "tutorialSidebar: [\n",
    "    'index', {'Guides': \n",
    "    \"\"\"\n",
    "            + str(parsed_guides)\n",
    "            + \"},\"\n",
    "            + \"{'API': [\"\n",
    "            + str(parsed_api)[1:-1]\n",
    "            + \"]},\"\n",
    "            + \"{'CLI': \"\n",
    "            + str(parsed_cli)\n",
    "            + \"},\"\n",
    "            + \"\"\"\n",
    "    \"LICENSE\",\n",
    "    \"CONTRIBUTING\",\n",
    "    \"CHANGELOG\",\n",
    "],\n",
    "};\"\"\"\n",
    "        )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3743fd1c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "module.exports = {\n",
      "tutorialSidebar: [\n",
      "    'index', {'Guides': \n",
      "    [{'Writing services': ['guides/Guide_05_Lifespan_Handler', 'guides/Guide_07_Encoding_and_Decoding_Messages_with_FastKafka']}, {'Testing': ['guides/Guide_31_Using_redpanda_to_test_fastkafka']}, {'Documentation generation': ['guides/Guide_04_Github_Actions_Workflow']}]},{'API': ['api/fastkafka/FastKafka', 'api/fastkafka/KafkaEvent', {'encoder': ['api/fastkafka/encoder/AvroBase', 'api/fastkafka/encoder/json_decoder', 'api/fastkafka/encoder/json_encoder']}, {'testing': ['api/fastkafka/testing/ApacheKafkaBroker']}]},{'CLI': ['cli/fastkafka', 'cli/run_fastkafka_server_process']},\n",
      "    \"LICENSE\",\n",
      "    \"CONTRIBUTING\",\n",
      "    \"CHANGELOG\",\n",
      "],\n",
      "};\n"
     ]
    }
   ],
   "source": [
    "summary = \"\"\"- [FastKafka](index.md)\n",
    "- Guides\n",
    "    - Writing services\n",
    "        - [Lifespan Events](guides/Guide_05_Lifespan_Handler.md)\n",
    "        - [Encoding and Decoding Kafka Messages with FastKafka](guides/Guide_07_Encoding_and_Decoding_Messages_with_FastKafka.md)\n",
    "    - Testing\n",
    "        - [Using Redpanda to test FastKafka](guides/Guide_31_Using_redpanda_to_test_fastkafka.md)\n",
    "    - Documentation generation\n",
    "        - [Deploy FastKafka docs to GitHub Pages](guides/Guide_04_Github_Actions_Workflow.md)\n",
    "- API\n",
    "    - fastkafka\n",
    "        - [FastKafka](api/fastkafka/FastKafka.md)\n",
    "        - [KafkaEvent](api/fastkafka/KafkaEvent.md)\n",
    "        - encoder\n",
    "            - [AvroBase](api/fastkafka/encoder/AvroBase.md)\n",
    "            - [json_decoder](api/fastkafka/encoder/json_decoder.md)\n",
    "            - [json_encoder](api/fastkafka/encoder/json_encoder.md)\n",
    "        - testing\n",
    "            - [ApacheKafkaBroker](api/fastkafka/testing/ApacheKafkaBroker.md)\n",
    "- CLI\n",
    "    - [fastkafka](cli/fastkafka.md)\n",
    "    - [run_fastkafka_server_process](cli/run_fastkafka_server_process.md)\n",
    "- [Releases](CHANGELOG.md)\"\"\"\n",
    "\n",
    "with TemporaryDirectory() as directory:\n",
    "    with open(directory + \"/SUMMARY.md\", \"w\") as stream:\n",
    "        stream.write(summary)\n",
    "\n",
    "    generate_sidebar(\n",
    "        summary_file=directory + \"/SUMMARY.md\", target=directory + \"/test.js\"\n",
    "    )\n",
    "\n",
    "    with open(directory + \"/test.js\") as stream:\n",
    "        stream = stream.read()\n",
    "\n",
    "print(stream)\n",
    "assert (\n",
    "    stream\n",
    "    == \"\"\"module.exports = {\n",
    "tutorialSidebar: [\n",
    "    'index', {'Guides': \n",
    "    [{'Writing services': ['guides/Guide_05_Lifespan_Handler', 'guides/Guide_07_Encoding_and_Decoding_Messages_with_FastKafka']}, {'Testing': ['guides/Guide_31_Using_redpanda_to_test_fastkafka']}, {'Documentation generation': ['guides/Guide_04_Github_Actions_Workflow']}]},{'API': ['api/fastkafka/FastKafka', 'api/fastkafka/KafkaEvent', {'encoder': ['api/fastkafka/encoder/AvroBase', 'api/fastkafka/encoder/json_decoder', 'api/fastkafka/encoder/json_encoder']}, {'testing': ['api/fastkafka/testing/ApacheKafkaBroker']}]},{'CLI': ['cli/fastkafka', 'cli/run_fastkafka_server_process']},\n",
    "    \"LICENSE\",\n",
    "    \"CONTRIBUTING\",\n",
    "    \"CHANGELOG\",\n",
    "],\n",
    "};\"\"\"\n",
    "), stream"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "975a9af5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "def _get_markdown_filenames_from_sidebar(sidebar_file_path: str) -> List[str]:\n",
    "    \"\"\"Get a list of Markdown filenames included in the sidebar.\n",
    "\n",
    "    Args:\n",
    "        sidebar_file_path: The path to the sidebar file.\n",
    "\n",
    "    Returns:\n",
    "        A list of Markdown filenames included in the sidebar.\n",
    "    \"\"\"\n",
    "    with open(sidebar_file_path, \"r\") as file:\n",
    "        file_content = file.read()\n",
    "\n",
    "        pattern = r\"tutorialSidebar:\\s*(\\[.*\\])\\s*,\\s*\\n?\\s*};\"\n",
    "        match = re.search(pattern, file_content, re.DOTALL)\n",
    "        all_sidebar_files = ast.literal_eval(match.group(1)) if match else []\n",
    "        markdown_filenames = [\n",
    "            f\"{v}.md\" for v in all_sidebar_files if isinstance(v, str)\n",
    "        ]\n",
    "        return markdown_filenames"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "711b627a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['CONTRIBUTING.md', 'LICENSE.md', 'index.md']\n"
     ]
    }
   ],
   "source": [
    "with TemporaryDirectory() as d:\n",
    "    docs_path = Path(d) / \"docusaurus\" / \"docs\"\n",
    "    docs_path.mkdir(parents=True)\n",
    "\n",
    "    sidebar_file_path = Path(d) / \"docusaurus\" / \"sidebar.js\"\n",
    "    with open(sidebar_file_path, \"w\") as f:\n",
    "        f.write(\n",
    "            \"\"\"module.exports = {\n",
    "tutorialSidebar: [\n",
    "    'index', {'Guides': \n",
    "    [{'Writing services': ['guides/Guide_05_Lifespan_Handler', 'guides/Guide_07_Encoding_and_Decoding_Messages_with_FastKafka']}, {'Testing': ['guides/Guide_31_Using_redpanda_to_test_fastkafka']}, {'Documentation generation': ['guides/Guide_04_Github_Actions_Workflow']}]},{'API': ['api/fastkafka/FastKafka', 'api/fastkafka/KafkaEvent', {'encoder': ['api/fastkafka/encoder/AvroBase', 'api/fastkafka/encoder/json_decoder', 'api/fastkafka/encoder/json_encoder']}, {'testing': ['api/fastkafka/testing/ApacheKafkaBroker']}]},{'CLI': ['cli/fastkafka', 'cli/run_fastkafka_server_process']},\n",
    "    \"LICENSE\",\n",
    "    \"CONTRIBUTING\",\n",
    "],\n",
    "};\"\"\"\n",
    "        )\n",
    "        \n",
    "    expected = [\"index.md\", \"LICENSE.md\", \"CONTRIBUTING.md\"]\n",
    "    actual = _get_markdown_filenames_from_sidebar(str(sidebar_file_path))\n",
    "    print(sorted(actual))\n",
    "    assert sorted(actual) == sorted(expected), actual"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d52d7808",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def _delete_files(files: List[Path]) -> None:\n",
    "    \"\"\"Deletes a list of files.\n",
    "\n",
    "    Args:\n",
    "        files: A list of Path objects representing the files to be deleted.\n",
    "\n",
    "    Raises:\n",
    "        OSError: If an error occurs while deleting a file.\n",
    "\n",
    "    \"\"\"\n",
    "    for file in files:\n",
    "        try:\n",
    "            file.unlink()\n",
    "        except OSError as e:\n",
    "            typer.echo(\n",
    "                f\"Error deleting files from docusaurus/docs directory. Could not delete file: {file} - {e}\"\n",
    "            )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b09c055e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error deleting files from docusaurus/docs directory. Could not delete file: /tmp/tmpor98b7dh/docusaurus/docs/t.txt - [Errno 2] No such file or directory: '/tmp/tmpor98b7dh/docusaurus/docs/t.txt'\n"
     ]
    }
   ],
   "source": [
    "with TemporaryDirectory() as d:\n",
    "    sample_file = Path(d) / \"docusaurus\" / \"docs\" / \"t.txt\"\n",
    "    _delete_files([sample_file])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "33356232",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[]\n"
     ]
    }
   ],
   "source": [
    "with TemporaryDirectory() as d:\n",
    "    docs_path = Path(d) / \"docusaurus\" / \"docs\"\n",
    "    docs_path.mkdir(parents=True)\n",
    "    \n",
    "    sample_file = docs_path / \"file.md\"\n",
    "    with open(sample_file, \"w\") as f:\n",
    "        f.write(\"sample text\")\n",
    "\n",
    "    _delete_files([sample_file])\n",
    "    actual = [file_path for file_path in Path(docs_path).glob(\"*.md\")]\n",
    "    print(actual)\n",
    "    assert actual == []"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a8a8e858",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "def delete_unused_markdown_files_from_sidebar(\n",
    "    docs_path: str, sidebar_file_path: str\n",
    ") -> None:\n",
    "    \"\"\"Delete the markdown files from the docs directory that are not present in the sidebar.\n",
    "\n",
    "    Args:\n",
    "        docs_path: Path to the directory containing the markdown files.\n",
    "        sidebar_file_path: Path to the sidebar file.\n",
    "    \"\"\"\n",
    "    md_filenames_in_sidebar = _get_markdown_filenames_from_sidebar(\n",
    "        str(sidebar_file_path)\n",
    "    )\n",
    "    if len(md_filenames_in_sidebar) > 0:\n",
    "        all_md_files_in_docs_dir = [\n",
    "            file_path for file_path in Path(docs_path).glob(\"*.md\")\n",
    "        ]\n",
    "        md_files_in_sidebar = [Path(docs_path) / f for f in md_filenames_in_sidebar]\n",
    "        md_files_to_delete = list(\n",
    "            set(all_md_files_in_docs_dir) - set(md_files_in_sidebar)\n",
    "        )\n",
    "        _delete_files(md_files_to_delete)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "383051ae",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[Path('/tmp/tmp4lrwkf_6/docusaurus/docs/CONTRIBUTING.md'), Path('/tmp/tmp4lrwkf_6/docusaurus/docs/LICENSE.md'), Path('/tmp/tmp4lrwkf_6/docusaurus/docs/index.md')]\n"
     ]
    }
   ],
   "source": [
    "with TemporaryDirectory() as d:\n",
    "    docs_path = Path(d) / \"docusaurus\" / \"docs\"\n",
    "    docs_path.mkdir(parents=True)\n",
    "\n",
    "    api_path = docs_path / \"api\"\n",
    "    api_path.mkdir(parents=True)\n",
    "\n",
    "    nested_api_path = api_path / \"fastKafka\"\n",
    "    nested_api_path.mkdir(parents=True)\n",
    "\n",
    "    for p in [api_path, nested_api_path]:\n",
    "        with open((p / \"file.md\"), \"w\") as f:\n",
    "            f.write(\"sample text\")\n",
    "\n",
    "    markdown_files = [\n",
    "        docs_path / \"index.md\",\n",
    "        docs_path / \"LICENSE.md\",\n",
    "        docs_path / \"CONTRIBUTING.md\",\n",
    "        docs_path / \"Non_sidebar_file_1.md\",\n",
    "        docs_path / \"Non_sidebar_file_2.md\",\n",
    "        docs_path / \"Non_sidebar_file_3.md\",\n",
    "    ]\n",
    "\n",
    "    for f in markdown_files:\n",
    "        with open(f, \"w\") as f:\n",
    "            f.write(\"sample text\")\n",
    "\n",
    "    sidebar_file_path = Path(d) / \"docusaurus\" / \"sidebar.js\"\n",
    "    with open(sidebar_file_path, \"w\") as f:\n",
    "        f.write(\n",
    "            \"\"\"module.exports = {\n",
    "tutorialSidebar: [\n",
    "    'index', {'Guides': \n",
    "    [{'Writing services': ['guides/Guide_05_Lifespan_Handler', 'guides/Guide_07_Encoding_and_Decoding_Messages_with_FastKafka']}, {'Testing': ['guides/Guide_31_Using_redpanda_to_test_fastkafka']}, {'Documentation generation': ['guides/Guide_04_Github_Actions_Workflow']}]},{'API': ['api/fastkafka/FastKafka', 'api/fastkafka/KafkaEvent', {'encoder': ['api/fastkafka/encoder/AvroBase', 'api/fastkafka/encoder/json_decoder', 'api/fastkafka/encoder/json_encoder']}, {'testing': ['api/fastkafka/testing/ApacheKafkaBroker']}]},{'CLI': ['cli/fastkafka', 'cli/run_fastkafka_server_process']},\n",
    "    \"LICENSE\",\n",
    "    \"CONTRIBUTING\",\n",
    "],\n",
    "};\"\"\"\n",
    "        )\n",
    "        \n",
    "    expected = [\n",
    "        docs_path / \"index.md\",\n",
    "        docs_path / \"LICENSE.md\",\n",
    "        docs_path / \"CONTRIBUTING.md\",\n",
    "    ]\n",
    "\n",
    "    delete_unused_markdown_files_from_sidebar(str(docs_path), str(sidebar_file_path))\n",
    "    actual = [file_path for file_path in Path(docs_path).glob(\"*.md\")]\n",
    "    print(sorted(actual))\n",
    "    assert sorted(actual) == sorted(expected), actual"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "18b20253",
   "metadata": {},
   "outputs": [],
   "source": [
    "# | export\n",
    "\n",
    "\n",
    "def update_readme() -> None:\n",
    "    \"\"\"Update the readme file and fix the symbol links\"\"\"\n",
    "    cfg = get_config()\n",
    "    readme_path = cfg.config_path / \"README.md\"\n",
    "    nbdev_readme.__wrapped__()\n",
    "\n",
    "    with open(readme_path, \"r\", encoding=\"utf-8\") as f:\n",
    "        contents = f.read()\n",
    "\n",
    "    contents = update_default_symbol_links(\n",
    "        contents, NbdevLookup(incl_libs=cfg.lib_path.name), \"\", \"\", False\n",
    "    )\n",
    "    contents = _fix_symbol_links(contents, \"./\", cfg.doc_host, cfg.doc_baseurl, False)\n",
    "\n",
    "    with open(readme_path, \"w\", encoding=\"utf-8\") as f:\n",
    "        f.write(contents)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d591af2d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Next, an object of the\n",
      "    [`FastKafka`](https://airtai.github.io/fastkafka/fastkafka.html#fastkafka)\n",
      "    class is initialized with the minimum set of arguments.\n",
      "    The service can be tested using the [`Tester`](https://airtai.github.io/fastkafka/tester.html#tester)\n",
      "    instances\n",
      "    \n"
     ]
    }
   ],
   "source": [
    "@contextmanager\n",
    "def mock_nbdev_readme(mock_contents, d):\n",
    "    with patch('__main__.nbdev_readme') as mock_nbdev_readme:\n",
    "        mock_nbdev_readme.__wrapped__ = MagicMock()\n",
    "        with open((Path(d) / \"README.md\"), \"w\", encoding=\"utf-8\") as f:\n",
    "            f.write(mock_contents)\n",
    "        \n",
    "        yield\n",
    "        \n",
    "        \n",
    "\n",
    "_mock_nbdev_readme_return_value = \"\"\"Next, an object of the\n",
    "    [`FastKafka`](https://airtai.github.io/fastkafka/fastkafka.html#fastkafka)\n",
    "    class is initialized with the minimum set of arguments.\n",
    "    The service can be tested using the [`Tester`](https://airtai.github.io/fastkafka/tester.html#tester)\n",
    "    instances\n",
    "    \"\"\"\n",
    "\n",
    "with TemporaryDirectory() as d:\n",
    "    with mock_nbdev_readme(_mock_nbdev_readme_return_value, d):\n",
    "        nbdev_readme.__wrapped__()\n",
    "        \n",
    "        with open((Path(d) / \"README.md\"), \"r\", encoding=\"utf-8\") as f:\n",
    "            contents = f.read()\n",
    "        \n",
    "        print(contents)\n",
    "        assert contents == _mock_nbdev_readme_return_value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "190b0190",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "/tmp/tmp0a889_8g\n"
     ]
    }
   ],
   "source": [
    "class dotdict(dict):\n",
    "    \"\"\"dot.notation access to dictionary attributes\"\"\"\n",
    "    __getattr__ = dict.get\n",
    "    __setattr__ = dict.__setitem__\n",
    "    __delattr__ = dict.__delitem__\n",
    "\n",
    "\n",
    "@contextmanager\n",
    "def mock_get_config(d):\n",
    "    with patch(\"__main__.get_config\") as mock_get_config:\n",
    "        d = {\n",
    "            \"config_path\": Path(d),\n",
    "            \"doc_host\": \"https://airtai.github.io\",\n",
    "            \"doc_baseurl\": \"/fastkafka\",\n",
    "            \"lib_path\": Path(d) / \"fastkafka\",\n",
    "        }\n",
    "        mock_get_config.return_value = dotdict(d)\n",
    "        yield\n",
    "\n",
    "\n",
    "with TemporaryDirectory() as d:\n",
    "    with mock_get_config(d):\n",
    "        cfg = get_config()\n",
    "\n",
    "        print(cfg.config_path)\n",
    "        assert cfg.config_path == Path(d)\n",
    "        assert cfg.doc_host == \"https://airtai.github.io\"\n",
    "        assert cfg.lib_path.name == \"fastkafka\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e6d48549",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Next, an object of the\n",
      "    [`FastKafka`](https://airtai.github.io/fastkafka/docs/api/fastkafka#fastkafka.FastKafka)\n",
      "    class is initialized with the minimum set of arguments.\n",
      "    The service can be tested using the [`Tester`](https://airtai.github.io/fastkafka/docs/api/fastkafka/testing/Tester#fastkafka.testing.Tester)\n",
      "    instances\n",
      "    \n"
     ]
    }
   ],
   "source": [
    "fixture = \"\"\"Next, an object of the\n",
    "    [`FastKafka`](https://airtai.github.io/fastkafka/fastkafka.html#fastkafka)\n",
    "    class is initialized with the minimum set of arguments.\n",
    "    The service can be tested using the [`Tester`](https://airtai.github.io/fastkafka/tester.html#tester)\n",
    "    instances\n",
    "    \"\"\"\n",
    "\n",
    "expected = \"\"\"Next, an object of the\n",
    "    [`FastKafka`](https://airtai.github.io/fastkafka/docs/api/fastkafka#fastkafka.FastKafka)\n",
    "    class is initialized with the minimum set of arguments.\n",
    "    The service can be tested using the [`Tester`](https://airtai.github.io/fastkafka/docs/api/fastkafka/testing/Tester#fastkafka.testing.Tester)\n",
    "    instances\n",
    "    \"\"\"\n",
    "\n",
    "\n",
    "with TemporaryDirectory() as d:\n",
    "    readme_path = Path(d) / \"README.md\"\n",
    "    with mock_get_config(d):\n",
    "        with mock_nbdev_readme(fixture, d):\n",
    "            update_readme()\n",
    "\n",
    "        with open(readme_path, \"r\", encoding=\"utf-8\") as f:\n",
    "            actual = f.read()\n",
    "        print(actual)\n",
    "        assert actual == expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "794269b1",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
